#! /usr/bin/env python2 -tt
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#

from itertools import chain, ifilter
from collections import namedtuple
import copy
import errno
import os
import re
import string

from thrift_compiler import frontend
# Easy access to the enum of t_base_type::t_base
from thrift_compiler.frontend import t_base
# Easy access to the enum of t_field::e_req
from thrift_compiler.frontend import e_req
# Easy access to the enum of t_const_value::t_const_value_type
from thrift_compiler.frontend import e_const_value_type as e_cv_type

from thrift_compiler.generate import t_generator

from thrift_compiler.generate.t_cpp_context import (
    CppOutputContext,
    CppPrimitiveFactory,
)
from thrift_compiler.generate.t_output_aggregator import get_global_scope
from thrift_compiler.generate.t_output_aggregator import out
from thrift_compiler.generate.t_output import IndentedOutput

# TODO move from here
class OrderedDict(dict):
    def __init__(self, *a, **b):
        dict.__init__(self, *a, **b)
        self._order = []

    def __setitem__(self, index, item):
        dict.__setitem__(self, index, item)
        self._order.append(index)

    def iteritems(self):
        return ((key, self[key]) for key in self._order)

# Same as map.get, but works for almost-dictionary-like types
# (like the ones generated by boost.python)
def _map_get(map, key, default=None):
    return map[key] if key in map else default

def _lift_unit(typ):
    if typ == 'void':
        return 'folly::Unit'
    return typ

# ---------------------------------------------------------------
# Generator
# ---------------------------------------------------------------

class CompilerError(RuntimeError):
    pass

SerializedFieldOptions = namedtuple('SerializedFieldOptions',
        ['has_serialized_fields', 'keep_unknown_fields'])

class CppGenerator(t_generator.Generator):
    '''
    Plain ol' c++ generator
    Note: this is NOT legitimacy incarnate
    '''

    # Protocols to generate client/server code for.
    protocols = [("binary", "BinaryProtocol", "T_BINARY_PROTOCOL"),
                 ("compact", "CompactProtocol", "T_COMPACT_PROTOCOL")]
    short_name = 'cpp2'
    long_name = 'C++ version 2'
    supported_flags = {
        'include_prefix': 'Use full include paths in generated files.',
        'terse_writes': 'Avoid emitting unspec fields whose values are default',
        'stack_arguments': 'Pass arguments on stack instead of heap',
        'process_in_event_base': 'Process request in event base thread',
        'frozen2': 'enable frozen structures',
        'json': 'enable simple json protocol',
        'implicit_templates' : 'templates are instantiated implicitly' +
                               'instead of explicitly',
        'optionals': "produce folly::Optional<...> for optional members",
        'reflection': 'generate static reflection metadata',
        'only_reflection': 'Only generate static reflection metadata',
        'lean_mean_meta_machine': 'use templated Fatal metadata based codegen',
        'no_getters_setters': "Don't produce get_/set_ methods",
    }
    _out_dir_base = 'gen-cpp2'

    _base_to_cpp_typename = {
        t_base.void: 'void',
        t_base.string: 'std::string',
        t_base.bool: 'bool',
        t_base.byte: 'int8_t',
        t_base.i16: 'int16_t',
        t_base.i32: 'int32_t',
        t_base.i64: 'int64_t',
        t_base.double: 'double',
        t_base.float: 'float',
    }

    _serialized_fields_name = '__serialized'
    _serialized_fields_type = 'apache::thrift::CloneableIOBuf'
    _serialized_fields_protocol_name = '__serialized_protocol'

    def __init__(self, *args, **kwargs):
        # super constructor
        t_generator.Generator.__init__(self, *args, **kwargs)

        prefix = self._flags.get('include_prefix')
        if isinstance(prefix, basestring):
            self.program.include_prefix = prefix
        terse_writes = self._flags.get('terse_writes')
        self.safe_terse_writes = (terse_writes == 'safe')

        if self.flag_json:
            self.protocols = copy.deepcopy(CppGenerator.protocols)
            self.protocols.append(
                ("simple_json", "SimpleJSONProtocol", "T_SIMPLE_JSON_PROTOCOL"))

    def _base_type_name(self, tbase):
        if tbase in self._base_to_cpp_typename:
            return self._base_to_cpp_typename[tbase]
        raise CompilerError('no C++ base type name for base type ' + tbase)

    def _cpp_annotation(self, type, key, default=None):
        t = _map_get(type.annotations, 'cpp2.' + key)
        if t:
            return t
        t = _map_get(type.annotations, 'cpp.' + key)
        if t:
            return t
        return default

    def _has_cpp_annotation(self, type, key):
        return self._cpp_annotation(type, key) is not None

    def _cpp_type_name(self, type, default=None, direct=False):
        if direct and self._has_cpp_annotation(type, 'indirection'):
            return default
        else:
            return self._cpp_annotation(type, 'type', default)

    def _cpp_ref_type(self, type, name):
        # backward compatibility with 'ref' annotation
        if self._has_cpp_annotation(type, 'ref'):
            return 'std::unique_ptr<{0}>'.format(name)

        ref_type = self._cpp_annotation(type, 'ref_type')
        if ref_type is None:
            return None

        # useful aliases
        if ref_type == 'unique':
            return 'std::unique_ptr<{0}>'.format(name)
        if ref_type == 'shared':
            return 'std::shared_ptr<{0}>'.format(name)
        if ref_type == 'shared_const':
            return 'std::shared_ptr<const {0}>'.format(name)

        # use custom pointer type
        return '{0}<{1}>'.format(ref_type, name)

    def _apply_unique_ptr_hack(self, type):
        return self._cpp_ref_type(type, '') == 'std::unique_ptr<>'

    def _nested_containers(self, ttype):
        ttype = self._get_true_type(ttype)
        if ttype.is_container:
            if ttype.is_map:
                tmap = ttype.as_map
                return '_rk' + self._nested_containers(tmap.key_type) + '_rv' + self._nested_containers(tmap.value_type)
            elif ttype.is_set:
                tset = ttype.as_set
                return '_r' + self._nested_containers(tset.elem_type)
            elif ttype.is_list:
                tlist = ttype.as_list
                return '_r' + self._nested_containers(tlist.elem_type)
        else:
            return ''

    def _type_name(self, ttype, in_typedef=False,
                   arg=False, scope=None, unique=False, direct=False):
        unique = unique and not self.flag_stack_arguments
        if ttype.is_stream:
            ttype = ttype.as_stream.elem_type
        if ttype.is_base_type:
            # cast it
            btype = ttype.as_base_type
            bname = self._base_type_name(btype.base)
            if arg and ttype.is_string:
                return self._reference_name(bname, unique)
            return self._cpp_type_name(ttype, bname, direct=direct)
        # Check for a custom overloaded C++ name
        if ttype.is_container:
            tcontainer = ttype.as_container
            inner_types = None
            template = self._cpp_annotation(tcontainer, 'template')
            cname = self._cpp_type_name(tcontainer)
            if cname:
                pass
            elif ttype.is_map:
                tmap = ttype.as_map
                if template:
                    cname = template
                elif tmap.is_unordered:
                    cname = 'std::unordered_map'
                else:
                    cname = 'std::map'
                inner_types = [self._type_name(tmap.key_type, in_typedef,
                                               scope=scope),
                               self._type_name(tmap.value_type, in_typedef,
                                               scope=scope)]
                cname = cname + '<{0}, {1}>'
            elif ttype.is_set:
                tset = ttype.as_set
                if template:
                    cname = template
                elif tset.is_unordered:
                    cname = 'std::unordered_set'
                else:
                    cname = 'std::set'
                inner_types = [self._type_name(tset.elem_type, in_typedef,
                                               scope=scope)]
                cname = cname + "<{0}>"
            elif ttype.is_list:
                cname = 'std::vector'
                if template:
                    cname = template
                tlist = ttype.as_list
                inner_types = [self._type_name(tlist.elem_type, in_typedef,
                                               scope=scope)]
                cname = cname + "<{0}>"
            if inner_types:
                cname = cname.format(*inner_types)
            if arg:
                return self._reference_name(cname, unique)
            else:
                return cname

        if in_typedef and (ttype.is_struct or ttype.is_xception) and \
                ttype.program == self._program:
            scope('class ' + ttype.name + ';')

        tname = self._cpp_type_name(ttype)
        if not tname:
            # Check if it needs to be namespaced
            program = ttype.program
            if program is not None:
                tname = self._namespace_prefix(self._get_namespace(program)) + \
                        ttype.name
            else:
                tname = ttype.name

        if arg and self._is_complex_type(ttype):
            return self._reference_name(tname, unique)
        else:
            return tname

    def _field_type_name(self, field, type):
        type_name = self._type_name(type)
        ref_type_name = self._cpp_ref_type(field, type_name)
        if ref_type_name is not None:
            return ref_type_name
        else:
            return type_name

    def _is_orderable_type(self, ttype):
        if ttype.is_base_type:
            return True
        elif ttype.is_enum:
            return True
        elif ttype.is_struct or ttype.is_xception:
            for m in ttype.as_struct.members:
                if m.req == e_req.optional \
                  or self._has_cpp_annotation(m.type, 'template') \
                  or not self._is_orderable_type(m.type):
                    return False
            return True
        elif ttype.is_map and not ttype.as_map.is_unordered:
            return self._is_orderable_type(ttype.as_map.key_type) \
                    and self._is_orderable_type(ttype.as_map.value_type)
        elif ttype.is_set and not ttype.as_set.is_unordered:
            return self._is_orderable_type(ttype.as_set.elem_type)
        elif ttype.is_list:
            return self._is_orderable_type(ttype.as_list.elem_type)
        else:
            return False

    def _get_cache_key(self, f):
        foundMember = None
        for member in f.arglist.members:
            if self._has_cpp_annotation(member, "cache"):
                if foundMember is not None:
                    raise CompilerError('Multiple cache \
                            annotations are not allowed')
                else:
                    foundMember = member
        return foundMember

    def _is_reference(self, f):
        return self._cpp_ref_type(f, '') is not None

    def _is_const_shared_ptr(self, f):
        return self._cpp_ref_type(f, '') == 'std::shared_ptr<const >'

    def _is_optional_wrapped(self, f):
        return f.req == e_req.optional and self.flag_optionals

    def _has_isset(self, f):
        return not self._is_reference(f) and f.req != e_req.required \
            and not self._is_optional_wrapped(f)

    # noncopyable is a hack to support gcc < 4.8, where declaring a constructor
    # as defaulted tries to generate it, even though it should be deleted.
    def _is_copyable_struct(self, ttype):
        assert ttype.is_struct or ttype.is_xception or ttype.is_union
        return not self._has_cpp_annotation(ttype, "noncopyable")

    def _is_comparable_struct(self, ttype):
        assert ttype.is_struct or ttype.is_xception or ttype.is_union
        return not self._has_cpp_annotation(ttype, "noncomparable")

    def _is_noex_move_ctor_struct(self, ttype):
        return ttype.is_struct and \
            self._has_cpp_annotation(ttype, "noexcept_move_ctor")

    def _reference_name(self, name, unique):
        if unique:
            return "std::unique_ptr<{0}>".format(name)
        else:
            return "const {0}&".format(name)

    def _get_namespace(self, program=None):
        if program == None:
            program = self._program
        ns = program.get_namespace('cpp2')
        if ns:
            return ns
        ns = program.get_namespace('cpp')
        parts = filter(None, ns.split('.'))
        parts.append('cpp2')
        return '.'.join(parts)

    def _namespace_prefix(self, ns):
        'Return the absolute c++ prefix for the .-separated namespace param'
        prefix = ' ::' + '::'.join(ns.split('.'))
        if len(ns) > 0:
            prefix += '::'
        return prefix

    def _is_complex_type(self, ttype):
        ttype = self._get_true_type(ttype)
        return ttype.is_container or \
               ttype.is_struct or \
               ttype.is_xception or \
               ttype.is_base_type and \
                    ttype.as_base_type.base == frontend.t_base.string

    def _get_true_type(self, ttype):
        'Get the true type behind a series of typedefs and streams'
        while ttype.is_typedef or ttype.is_stream:
            if ttype.is_typedef:
                ttype = ttype.as_typedef.type
            else:
                ttype = ttype.as_stream.elem_type
        return ttype

    def _is_stream_type(self, ttype):
        while ttype.is_typedef:
            ttype = ttype.as_typedef.type
        return ttype.is_stream

    def _type_access_suffix(self, ttype):
        if not ttype.is_typedef:
            return ''
        return self._cpp_annotation(ttype.as_typedef.type, 'indirection', '')

    def _gen_forward_declaration(self, tstruct):
        out("class {0};".format(tstruct.name))

    def _get_handler_callback_class(self, function):
        if self._is_complex_type(function.returntype) and \
                not self.flag_stack_arguments:
            rettype = self._type_name(function.returntype)
            rettype = 'std::unique_ptr<' + rettype + '>'
        else:
            rettype = self._type_name(function.returntype)
        cb = 'StreamingHandlerCallback' if self._is_stream_type(function.returntype) \
                else 'HandlerCallback'
        return 'apache::thrift::{0}<{1}>'.format(cb, rettype)

    def _get_presult_success(self):
        return 'result.get<0>().value'
    def _get_presult_success_isset(self, value_if_set=None):
        if value_if_set is None:
            return 'result.getIsSet(0)'
        else:
            return 'result.setIsSet(0, {0})'.format(value_if_set)
    def _get_presult_exception(self, ex_idx, e):
        return 'result.get<{0}>().ref()'.format(ex_idx)
    def _get_presult_exception_isset(self, ex_idx, e, value_if_set=None):
        if value_if_set is None:
            return 'result.getIsSet({0})'.format(ex_idx)
        else:
            return 'result.setIsSet({0}, {1})'.format(ex_idx, value_if_set)
    def _get_pargs_field(self, idx, f, pointer=True):
        return 'args.get<{0}>().{1}'.format(idx,
                'value' if pointer else 'ref()')

    def _generate_enum_constant_list(self, enum, constants, quote_names,
                                      include_values):
        if include_values:
            name_format = '{0.name}'
            value_format = ' = {0.value}'
        else:
            value_format = ''
            name_format = quote_names and '"{0.name}"' or enum + "::{0.name}"
        return ',\n'.join((name_format + value_format).format(const) \
                          for const in constants)

    def _generate_enum(self, tenum):
        '''
        Generates code for an enumerated type. In C++, this is essentially the
        same as the thrift defn itself, using the enum keyword in C++.

        @param  tenum    The enumeration
        '''
        constants = tenum.constants
        # get the scope inside the namespace definitions
        s = self._types_scope
        enum_type = self._cpp_annotation(tenum, "enum_type")
        enum_type_s = "" if enum_type is None else " : " + enum_type
        primitive = s.defn('enum class {0.name}{1}'.format(tenum, enum_type_s),
                           in_header=True)
        # what follows after the closing brace
        primitive.epilogue = ';\n\n'
        with primitive:
            out(self._generate_enum_constant_list(tenum.name, constants,
                    quote_names=False, include_values=True))
        map_factory = '_{0}_EnumMapFactory'.format(tenum.name)
        s('using {0} = apache::thrift::detail::TEnumMapFactory<{1}, {1}>;'
            .format(map_factory, tenum.name))
        s.extern(
            'const {0}::ValuesToNamesMapType _{1}_VALUES_TO_NAMES'
            .format(map_factory, tenum.name),
            value=(
                ' = {0}::makeValuesToNamesMap();'
                .format(map_factory)))
        s.extern(
            'const {0}::NamesToValuesMapType _{1}_NAMES_TO_VALUES'
            .format(map_factory, tenum.name),
            value=(
                ' = {0}::makeNamesToValuesMap();'
                .format(map_factory)))
        s()
        s.impl('\n')

        if self._cpp_annotation(tenum, 'declare_bitwise_ops'):
            for op in ['&', '|', '^']:
                with s.defn(
                        '{0} {{name}}({0} a, {0} b)'.format(tenum.name),
                        name='operator{0}'.format(op),
                        in_header=True,
                        modifiers='inline constexpr'):
                    out('using E = {0};'.format(tenum.name))
                    out('using U = std::underlying_type_t<E>;')
                    out('return static_cast<E>('
                        'static_cast<U>(a) {0} static_cast<U>(b));'.format(op))
                out('#if __cplusplus >= 201402L')
                with s.defn(
                        '{0}& {{name}}({0}& a, {0} b)'.format(tenum.name),
                        name='operator{0}='.format(op),
                        in_header=True,
                        modifiers='inline constexpr'):
                    out('return a = a {0} b;'.format(op))
                out('#else')
                with s.defn(
                        '{0}& {{name}}({0}& a, {0} b)'.format(tenum.name),
                        name='operator{0}='.format(op),
                        in_header=True,
                        modifiers='inline'):
                    out('return a = a {0} b;'.format(op))
                out('#endif')
            for op in ['~']:
                with s.defn(
                        '{0} {{name}}({0} a)'.format(tenum.name),
                        name='operator{0}'.format(op),
                        in_header=True,
                        modifiers='inline constexpr'):
                    out('using E = {0};'.format(tenum.name))
                    out('using U = std::underlying_type_t<E>;')
                    out('return static_cast<E>({0}static_cast<U>(a));'.format(op))

        # specialize TEnumTraits
        s.release()

        ns = self._namespace_prefix(self._get_namespace())
        fullName = ns + tenum.name
        minName = None
        maxName = None

        if len(constants) > 0:
            sortedConstants = sorted(constants, key=lambda c: c.value)
            minName = sortedConstants[0].name
            maxName = sortedConstants[-1].name

        self._generate_hash_equal_to(tenum)

        with self._types_global.namespace('apache.thrift').scope:
            out(
                'template <> struct TEnumDataStorage<{fullName}>;'
                .format(**locals()))
            # TEnumTraits<T> class member specializations
            storage_fullname = (
                '{ns}_{name}EnumDataStorage'.format(ns=ns, name=tenum.name))

            def storage_range_of(field):
                if not constants:
                    return '{}'
                else:
                    return 'folly::range({}::{})'.format(
                        storage_fullname, field)
            # TEnumTraits<T>::size
            out(
                'template <> const std::size_t '
                'TEnumTraits<{fullName}>::size;'
                .format(**locals()))
            out().impl(
                'template <> const std::size_t '
                'TEnumTraits<{fullName}>::size = {size};'
                .format(size=len(constants), **locals()))
            # TEnumTraits<T>::values
            out(
                'template <> const folly::Range<const {fullName}*> '
                'TEnumTraits<{fullName}>::values;'
                .format(**locals()))
            out().impl(
                'template <> const folly::Range<const {fullName}*> '
                'TEnumTraits<{fullName}>::values = {range};'
                .format(range=storage_range_of('values'), **locals()))
            # TEnumTraits<T>::names
            out(
                'template <> const folly::Range<const folly::StringPiece*> '
                'TEnumTraits<{fullName}>::names;'
                .format(**locals()))
            out().impl(
                'template <> const folly::Range<const folly::StringPiece*> '
                'TEnumTraits<{fullName}>::names = {range};'
                .format(range=storage_range_of('names'), **locals()))
            # TEnumTraits<T>::findName()
            with out().defn('template <> const char* TEnumTraits<{fullName}>::'
                        'findName({fullName} value)'.format(**locals()),
                        name='findName'):
                out('static auto const map = folly::Indestructible<{0}{1}'
                    '::ValuesToNamesMapType>{{{0}{1}::makeValuesToNamesMap()}};'
                    .format(ns, map_factory))
                out('return findName(*map, value);')
            # TEnumTraits<T>::findValue()
            with out().defn('template <> bool TEnumTraits<{fullName}>::'
                        'findValue(const char* name, {fullName}* outValue)'.
                        format(**locals()), name='findValue'):
                out('static auto const map = folly::Indestructible<{0}{1}'
                    '::NamesToValuesMapType>{{{0}{1}::makeNamesToValuesMap()}};'
                    .format(ns, map_factory))
                out('return findValue(*map, name, outValue);')
            # TEnumTraits<T> class member specializations
            if minName is not None and maxName is not None:
                with out().defn('template <> inline constexpr {fullName} '
                            'TEnumTraits<{fullName}>::min()'
                            .format(**locals()), name='min', in_header=True):
                    out('return {fullName}::{minName};'.format(**locals()))
                with out().defn('template <> inline constexpr {fullName} '
                            'TEnumTraits<{fullName}>::max()'
                            .format(**locals()), name='max', in_header=True):
                    out('return {fullName}::{maxName};'.format(**locals()))
        s = self._types_scope = \
                s.namespace(self._get_namespace()).scope
        s.acquire()

    def _generate_typedef(self, ttypedef):
        # We write all of these to the types scope
        scope = self._types_scope
        the_type = self._type_name(ttypedef.type, in_typedef=True, scope=scope)
        txt = 'typedef {0} {1};\n\n'.format(the_type, ttypedef.symbolic)
        scope(txt)

        if self._should_generate_hash_equal_to(ttypedef):
            # We're at types scope now
            scope.release()

            # std::hash and std::equal_to declarations
            self._generate_hash_equal_to(ttypedef)

            # Re-enter types scope, but we can't actually re-enter a scope,
            # so let's recreate it
            scope = self._types_scope = \
                    scope.namespace(self._get_namespace()).scope
            scope.acquire()

    def _declare_field(self, field, pointer=False, constant=False,
                        reference=False, optional_wrapped=False,
                        deprecated=False):
        # I removed the 'init' argument, as all inits happen in default
        # constructor
        result = ''
        if constant:
            result += 'const '
        result += self._field_type_name(field, field.type)
        if pointer:
            result += '*'
        if reference:
            result += '&'
        if optional_wrapped:
            result = "folly::Optional<" + result + ">"
        if deprecated:
            result += ' ' + deprecated
        result += ' ' + field.name
        if not reference:
            result += ';'
        return result

    def _type_to_enum(self, ttype):
        'Converts the parse type to a C++ enum string for a given type.'
        t = self._get_true_type(ttype)
        suffix = None
        if t.is_base_type:
            base = t.as_base_type.base
            if base == t_base.void:
                raise ValueError('NO T_VOID CONSTRUCT')
            switch = {
                t_base.string: 'T_STRING',
                t_base.bool: 'T_BOOL',
                t_base.byte: 'T_BYTE',
                t_base.i16: 'T_I16',
                t_base.i32: 'T_I32',
                t_base.i64: 'T_I64',
                t_base.double: 'T_DOUBLE',
                t_base.float: 'T_FLOAT',
            }
            suffix = switch[base]
        elif t.is_enum:
            suffix = 'T_I32'
        elif t.is_struct or t.is_xception:
            suffix = 'T_STRUCT'
        elif t.is_map:
            suffix = 'T_MAP'
        elif t.is_set:
            suffix = 'T_SET'
        elif t.is_list:
            suffix = 'T_LIST'
        if suffix is None:
            raise TypeError('INVALID TYPE IN type_to_enum: ' + t.name)
        return 'apache::thrift::protocol::' + suffix

    def _generate_data(self):
        context = self._make_context(
            self._program.name + '_data',
            is_types_context=True)
        sg = get_global_scope(CppPrimitiveFactory, context)
        sg('#include <array>')
        sg('#include <cstddef>')
        sg()
        sg('#include <thrift/lib/cpp/Thrift.h>')
        sg()
        sg('#include "{0}_types.h"'.format(
            self._with_include_prefix(self._program, self._program.name)))
        sg()
        for enum in self._program.enums:
            storage_name = "_{}EnumDataStorage".format(enum.name)
            sns = sg.namespace(self._get_namespace()).scope
            with sns:
                s = sns.cls('struct {}'.format(storage_name)).scope
                with s:
                    out('using type = {};'.format(enum.name))
                    out('static constexpr const std::size_t size = {};'
                        .format(len(enum.constants)))
                    out('static constexpr const '
                        'std::array<{}, {}> values = {{{{'
                        .format(enum.name, len(enum.constants)))
                    for c in enum.constants:
                        out('  {}::{},'.format(enum.name, c.name))
                    out('}};')
                    out('static constexpr const '
                        'std::array<folly::StringPiece, {}> names = {{{{'
                        .format(len(enum.constants)))
                    for c in enum.constants:
                        out('  "{}",'.format(c.name))
                    out('}};')
                sns.impl(
                    'constexpr const std::size_t {}::size;'
                    .format(storage_name))
                sns.impl(
                    'constexpr const '
                    'std::array<{}, {}> {}::values;'
                    .format(enum.name, len(enum.constants), storage_name))
                sns.impl(
                    'constexpr const '
                    'std::array<folly::StringPiece, {}> {}::names;'
                    .format(len(enum.constants), storage_name))
            sns = sg.namespace('apache.thrift').scope
            with sns:
                ns = self._namespace_prefix(self._get_namespace())
                out(
                    'template <> struct TEnumDataStorage<{}{}> {{'
                    .format(ns, enum.name))
                out(
                    '  using storage_type = {}{};'
                    .format(ns, storage_name))
                out('};')
        sg()
        sg.impl('\n')

    # =====================================================================
    # SERVICE INTERFACE
    # =====================================================================

    def _generate_async_client_h_shim(self, service):
        context = self._make_context(service.name + 'AsyncClient', impl=False)
        s = self._service_global = get_global_scope(CppPrimitiveFactory,
                                                    context)
        s.acquire()
        s('// Simple wrapper header to make interoperating between mstch')
        s('// and non mstch generated classes easier, without dependencies')
        s('// between the client -> server and server -> client. This is')
        s('// an internal implementation detail, and may be deleted at any')
        s('// time.')
        s('#include "{0}.h"'.format(
            self._with_include_prefix(self._program, service.name)))
        s.release()

    def _generate_service(self, service):
        self._generate_async_client_h_shim(service)

        # open files and instantiate outputs
        context = self._make_context(service.name, True, True, True)
        self._out_tcc = context.tcc
        self._additional_outputs = context.additional_outputs
        self._custom_protocol_h = context.custom_protocol_h
        s = self._service_global = get_global_scope(CppPrimitiveFactory,
                                                    context)
        # Enter the scope (prints guard)
        s.acquire()
        s('#include <thrift/lib/cpp2/ServiceIncludes.h>')
        s('#include <thrift/lib/cpp2/async/AsyncClient.h>')
        s('#include <thrift/lib/cpp2/async/HeaderChannel.h>')
        s('#include <thrift/lib/cpp/TApplicationException.h>')
        s('#include <thrift/lib/cpp2/async/FutureRequest.h>')
        s('#include <folly/futures/Future.h>')
        s('#include <thrift/lib/thrift/gen-cpp2/RpcMetadata_types.h>')
        print >>self._custom_protocol_h, \
                '#include "{0}"'.format(self._with_include_prefix(
                    self._program,
                    self._program.name + '_types_custom_protocol.h'))
        for _, b, _ in self.protocols:
            print >>context.additional_outputs[-1], \
                    '#include <thrift/lib/cpp2/protocol/{0}.h>'.format(b)
        print >>context.impl, '#include <thrift/lib/cpp2/protocol/Protocol.h>'
        for _, b, _ in self.protocols:
            print >>context.impl, \
                    '#include <thrift/lib/cpp2/protocol/{0}.h>'.format(b)
        s()
        # Include other Thrift includes
        for inc in self._program.includes:
            s('#include "{0}_types.h"' \
              .format(self._with_include_prefix(inc, inc.name)))
            print >>self._custom_protocol_h, \
                    '#include "{0}_types_custom_protocol.h"'.format(
                            self._with_include_prefix(inc, inc.name))
            if self.flag_implicit_templates:
                print >>self._out_tcc, '#include "{0}_types.tcc"'.format(
                    self._with_include_prefix(inc, inc.name))
        s()
        # Include custom headers
        for inc in self._program.cpp_includes:
            if inc.startswith('<'):
                s('#include {0}'.format(inc))
            else:
                s('#include "{0}"'.format(inc))

        if service.extends:
            s('#include "{0}.h"'.format(
                    self._with_include_prefix(service.extends.program,
                                              service.extends.name)))
            print >>self._custom_protocol_h, \
                    '#include "{0}_custom_protocol.h"'.format(
                            self._with_include_prefix(service.extends.program,
                                                      service.extends.name))
        # include our types last, which might depend on types in custom headers
        s()
        s('#include "{0}_types.h"'.format(
            self._with_include_prefix(self._program, self._program.name)))

        s()
        s('namespace folly { ')
        s('  class IOBuf;')
        s('  class IOBufQueue;')
        s('}')
        s('namespace apache { namespace thrift {')
        s('  class Cpp2RequestContext;')
        s('  class BinaryProtocolReader;')
        s('  class CompactProtocolReader;')
        s('  namespace transport { class THeader; }')
        s('}}')
        s()

        # Open namespace
        ns = s.namespace(self._get_namespace()).scope
        ns.acquire()

        self._generate_service_helpers(service, ns)
        self._generate_service_server_interface_async(service, ns)
        ns('class ' + service.name + 'AsyncProcessor;')
        self._generate_service_server_interface(service, ns)
        self._generate_service_server_null(service, ns)
        self._generate_processor(service, ns)
        self._generate_service_client(service, ns)

        # make sure that the main types namespace is closed
        ns.release()

        if self.flag_implicit_templates:
            # Include the types.tcc file from the types header file
            s()
            if self.flag_lean_mean_meta_machine:
                s('#include "{0}"'.format(self._fatal_header()))
            s('#include "{0}.tcc"'.format(
                self._with_include_prefix(self._program, service.name)))
        # Make sure file scope is closed.
        s.release()

    def _get_presult_object(self, service, function):
        result = frontend.t_struct(
            self.program,
            "{0}_{1}_presult".format(service.name, function.name))
        success = frontend.t_field(function.returntype, "success", 0)
        if not function.returntype.is_void:
            result.append(success)
        xs = function.xceptions
        for field in xs.members:
            result.append(field)
        return result



    def _generate_service_helpers(self, service, s):
        for function in service.functions:
            arglist = function.arglist
            def generate_pargs(suffix, pargs):
                flist = ['apache::thrift::FieldData<{0}, {1}, {2}{3}>'.format(
                            arg.key,
                            self._type_to_enum(arg.type),
                            self._type_name(arg.type),
                            suffix)
                                for arg in arglist.members]
                pargs = "{0}_{1}_{2}".format(service.name, function.name, pargs)
                flist.insert(0, 'false')
                fstr = ", ".join(flist)
                print >>self._out_tcc, \
                    'typedef apache::thrift::ThriftPresult<{0}> {1};'.format(
                        fstr, pargs)
            if self.flag_stack_arguments:
                generate_pargs('', 'args')
            generate_pargs('*', 'pargs')


            if not function.oneway:
                flist = ['apache::thrift::FieldData<{0}, '
                         'apache::thrift::protocol::T_STRUCT, {1}>'.format(
                            field.key, self._type_name(field.type))
                                for field in function.xceptions.members]
                if not function.returntype.is_void:
                    type = self._type_name(function.returntype)
                    ttype = self._type_to_enum(function.returntype)
                    flist.insert(0, 'apache::thrift::FieldData<0, {0}, {1}*>'
                        .format(ttype, type))

                presult = "{0}_{1}_presult".format(service.name, function.name)
                flist.insert(0, 'true')
                fstr = ", ".join(flist)

                print >>self._out_tcc, \
                    'typedef apache::thrift::ThriftPresult<{0}> {1};'.format(
                        fstr, presult)

    def _generate_service_client(self, service, s):
        classname = service.name + "AsyncClient"
        if service.extends:
            base_fullname = self._type_name(service.extends) + "AsyncClient"
        else:
            base_fullname = "apache::thrift::GeneratedAsyncClient"
        base_classname = base_fullname.rsplit("::", 1)[1]
        class_signature = (
            'class {0} : public {1}'.format(classname, base_fullname))
        with s.cls(class_signature):
            out().label('public:')

            out('using {0}::{1};'.format(base_fullname, base_classname))

            with out().defn('char const* {name}() const noexcept',
                    name='getServiceName',
                    override=True,
                    in_header=True):
                out("return \"{0}\";".format(service.name))

            # Write out all the functions
            for function in service.functions:
                self._generate_client_async_function(service, function)
                self._generate_client_async_function(service, function,
                                                     uses_rpc_options=True)
                self._generate_client_async_function(service, function,
                                                     uses_rpc_options=True,
                                                     uses_sync=True)

                if not self._is_stream_type(function.returntype):
                    self._generate_client_sync_function(service, function)
                    self._generate_client_sync_function(service, function,
                                                        uses_rpc_options=True)
                    self._generate_client_future_function(service, function)
                    self._generate_client_future_function(service, function,
                                                          uses_rpc_options=True)
                    if not function.oneway:
                        self._generate_client_future_function(
                                service, function,
                                uses_rpc_options=True, header=True)

                self._generate_client_folly_function(function)

                if self._is_stream_type(function.returntype):
                    self._generate_client_streaming_function(service, function)
                    self._generate_client_streaming_function(service, function,
                        uses_rpc_options=True)

                if not function.oneway:
                    self._generate_recv_functions(function)

                self._generate_templated_client_function(service, function)

                if not function.oneway:
                    self._generate_templated_recv_function(service, function)

    def _get_async_func_name(self, function):
        if self._is_processed_in_eb(function):
            return 'async_eb_' + function.name
        else:
            return 'async_tm_' + function.name

    def _generate_service_server_null(self, service, s):
        classname = service.name + "SvNull"
        if not service.extends:
            class_signature = "class " + classname + " : public " + \
                service.name + \
                "SvIf"
        else:
            class_signature = "class " + classname + " : public " + \
                service.name + "SvIf, virtual public " + \
                self._type_name(service.extends) + "SvIf"
        with s.cls(class_signature):
            out().label('public:')
            for function in service.functions:
                if not self._is_processed_in_eb(function) and \
                   not self._is_stream_type(function.returntype):
                    with out().defn(
                            self._get_process_function_signature(service,
                                                                 function,
                                                                 True),
                            name=function.name,
                            override=True):
                        if not function.oneway and \
                          not function.returntype.is_void and \
                          not self._is_complex_type(function.returntype):
                            out('return ' +
                              self._default_value(function.returntype) + ';')

    def _generate_service_server_interface_async(self, service, s):
        classname = service.name + "SvAsyncIf"
        class_signature = "class " + classname
        with s.cls(class_signature):
            out().label('public:')
            out().defn('~{0}()'.format(classname), name=classname,
                   modifiers='virtual',
                   in_header=True).scope.empty()
            for function in service.functions:
                out().defn(self._get_process_function_signature_async(service,
                                                                  function),
                       name=self._get_async_func_name(function),
                       modifiers='virtual',
                       pure_virtual=True)

                if not self._is_stream_type(function.returntype):
                    out().defn(self._get_process_function_signature_future(
                           service, function),
                           name="future_" + function.name,
                           modifiers='virtual',
                           pure_virtual=True)

    def _get_function_priority(self, service, function):
        if function.annotations is not None and \
                'priority' in function.annotations.annotations:
            return function.annotations.annotations['priority']
        elif 'priority' in service.annotations:
            return service.annotations['priority']
        else:
            return 'NORMAL'

    def _generate_service_server_interface(self, service, s):
        classname = service.name + "SvIf"
        if not service.extends:
            class_signature = "class " + classname + " : public " + \
                service.name + \
                "SvAsyncIf, public apache::thrift::ServerInterface"
        else:
            class_signature = "class " + classname + " : public " + \
                service.name + "SvAsyncIf, virtual public " + \
                self._type_name(service.extends) + "SvIf"
        with s.cls(class_signature):
            out().label('public:')
            out('typedef ' + service.name + 'AsyncProcessor ProcessorType;')
            with out().defn(
                    'std::unique_ptr<apache::thrift::AsyncProcessor>' +
                    ' {name}()',
                    name='getProcessor',
                    override=True):
                out('return std::make_unique<{klass}AsyncProcessor>(this);'
                    .format(klass=service.name))
            for function in service.functions:
                if not self._is_stream_type(function.returntype):
                    with out().defn(self._get_process_function_signature(service,
                                                                     function,
                                                                     True),
                                name=function.name,
                                modifiers='virtual'):
                        out('apache::thrift::detail::si::'
                            'throw_app_exn_unimplemented("{0}");'
                            .format(function.name))
                    self._generate_server_future_function(service, function)
                self._generate_server_async_function(service, function)

    def _generate_server_future_function(self, service, function):
        name = "future_" + function.name
        sig = self._get_process_function_signature_future(service, function)
        with out().defn(sig, name=name, override=True):
            if self.flag_stack_arguments:
                args = [m.name for m in function.arglist.members]
                if not self._is_complex_type(function.returntype):
                    f = "future"
                    c = "[&] {{ return {n}({a}); }}"
                else:
                    f = "future_returning"
                    args = ["_return"] + args
                    c = "[&]({r}& _return) {{ {n}({a}); }}"
            else:
                args = [
                    "std::move({n})".format(
                        n=m.name) if self._is_complex_type(m.type) else m.name
                    for m in function.arglist.members
                ]
                if not self._is_complex_type(function.returntype):
                    f = "future"
                    c = "[&] {{ return {n}({a}); }}"
                else:
                    f = "future_returning_uptr"
                    args = ["_return"] + args
                    c = "[&]({r}& _return) {{ {n}({a}); }}"
            s = "return {ns}::{f}(" + c + ");"
            ns = "apache::thrift::detail::si"
            r = self._type_name(function.returntype)
            out(s.format(ns=ns, f=f, n=function.name, r=r, a=", ".join(args)))

    def _generate_server_async_function_streaming(self, function):
        out('callback->exception(folly::make_exception_wrapper<'
            'apache::thrift::TApplicationException>('
            '"Function {0} is unimplemented"));'.format(function.name))

    def _generate_server_async_function_future(self, function):
        if self._is_stream_type(function.returntype):
            return self._generate_server_async_function_streaming(function)
        stack_args = self.flag_stack_arguments
        is_complex_type = lambda t: self._is_complex_type(t) and not stack_args
        if self._is_processed_in_eb(function):
            captures = ['this']
            captures.extend(
                '{n} = std::move({n})'.format(
                    n=arg.name) if is_complex_type(arg.type) else arg.name
                for arg in function.arglist.members
            )
            f = "async_eb_oneway" if function.oneway else "async_eb"
            c = ('[{0}]'.format(', '.join(captures)) +
                 "() mutable {{ return future_{n}({a}); }}")
        else:
            f = "async_tm_oneway" if function.oneway else "async_tm"
            c = "[&] {{ return future_{n}({a}); }}"
        s = "{ns}::{f}(this, std::move(callback), " + c + ");"
        args = [
            "std::move({n})".format(n=m.name)
            if is_complex_type(m.type) else m.name
            for m in function.arglist.members
        ]
        ns = "apache::thrift::detail::si"
        out(s.format(ns=ns, f=f, n=function.name, a=", ".join(args)))

    def _generate_server_async_function(self, service, function):
        with out().defn(
                self._get_process_function_signature_async(service, function),
                name=self._get_async_func_name(function),
                override=True):
            self._generate_server_async_function_future(function)

    def _get_process_function_signature_async(self, service, function):
        sig = 'void {name}('
        if function.oneway:
            sig += 'std::unique_ptr<apache::thrift::HandlerCallbackBase>' + \
                ' callback'
        else:
            sig += 'std::unique_ptr<{0}> callback'.format(
                    self._get_handler_callback_class(function))

        sig += self._argument_list(
            function.arglist,
            add_comma=True,
            unique=True,
            no_param_names=self._is_stream_type(function.returntype),
        )
        sig += ')'
        return sig

    def _get_process_function_signature_future(self, service, function):
        rettype = self._type_name(function.returntype)
        if self._is_complex_type(function.returntype) and \
                not self.flag_stack_arguments:
            rettype = 'std::unique_ptr<' + rettype + '>'
        sig = 'folly::Future<' + \
            _lift_unit(rettype) + '> {name}('

        sig += self._argument_list(function.arglist, False, unique=True)
        sig += ')'
        return sig

    def _get_process_function_signature(self, service, function,
                                        no_param_names=False):
        addcomma = False
        if not function.oneway:
            if self._is_complex_type(function.returntype):
                sig = 'void {name}' + '({0}& {1}'.format(
                    self._type_name(function.returntype),
                    '/*_return*/' if no_param_names else '_return')
                addcomma = True
            else:
                sig = self._type_name(function.returntype)
                sig += ' {name}('
        else:
            sig = 'void {name}('
        sig += self._argument_list(function.arglist, addcomma, unique=True,
                                   no_param_names=no_param_names)
        sig += ')'
        return sig

    def _generate_app_ex(self, service, errorstr, functionname, seqid, is_in_eb,
                         s, reqCtx, err_code=None, uex_ew=None):
        with out('if (req)'):
            out('LOG(ERROR) << {0} << " in function {1}";'.format(
                    errorstr, functionname))
            code = '' if err_code is None else \
                    'apache::thrift::TApplicationException::' \
                    'TApplicationExceptionType::' + err_code + ', '
            out('apache::thrift::TApplicationException x({0}{1});'.
                format(code, errorstr))
            if uex_ew:
                ctx = 'ctx'
                out('ctx->userExceptionWrapped(false, {});'.format(uex_ew))
                out('ctx->handlerErrorWrapped({});'.format(uex_ew))
            else:
                ctx = 'nullptr'
            out('folly::IOBufQueue queue = serializeException("{0}", &prot, {1}, {2}, '
                'x);'.format(functionname, seqid, ctx))
            out('queue.append(apache::thrift::transport::THeader::transform('
                'queue.move(), '
                '{0}->getHeader()->getWriteTransforms(), '
                '{0}->getHeader()->getMinCompressBytes()));'.format(reqCtx))
            if is_in_eb:
                out('req->sendReply(queue.move());')
            else:
                with out('eb->runInEventBaseThread('
                         '[queue = std::move(queue), '
                         'req = std::move(req)]'
                         '() mutable'):
                    out('req->sendReply(queue.move());')
                out(');')
            out('return;')
        with out('else'):
            out('LOG(ERROR) << {0} << " in oneway function {1}";'.format(
                    errorstr, functionname))

    def _generate_process_function(self, service, function):
        if function.oneway:
            if self._is_processed_in_eb(function):
                # Old clients may not send the special
                # oneway id, so we need to send a fake
                # response to them while in event base.
                with out('if (!req->isOneway())'):
                    out('req->sendReply(std::unique_ptr<folly::IOBuf>());')
        out("// make sure getConnectionContext is null")
        out("// so async calls don't accidentally use it")
        out('iface_->setConnectionContext(nullptr);')
        aprefix = 'uarg_'
        if self.flag_stack_arguments:
            out('{0}_{1}_args args;'.format(service.name, function.name))
        else:
            out('{0}_{1}_pargs args;'.format(service.name, function.name))
        for idx, field in enumerate(function.arglist.members):
            val = ""
            t = self._get_true_type(field.type)
            if t.is_base_type or t.is_enum:
                val = self._member_default_value(field)
            if self.flag_stack_arguments:
                pass
            elif self._is_complex_type(field.type):
                out('std::unique_ptr<{0}> {1}(new {0}({2}));'.format(
                        self._type_name(field.type), aprefix + field.name, val))
                out('{0} = {1}.get();'.format(
                        self._get_pargs_field(idx, field),
                        aprefix + field.name))
            else:
                # use uniform initialization syntax to avoid most vexing parse
                out('{0} {1}{{{2}}};'.format(
                        self._type_name(field.type), aprefix + field.name, val))
                out('{0} = &{1};'.format(
                        self._get_pargs_field(idx, field),
                        aprefix + field.name))
        out(('std::unique_ptr<apache::thrift::' +
           'ContextStack> c(this->getContextStack' +
           '(this->getServiceName(), "{0}.{1}", ctx));'
           ).format(service.name, function.name))
        out("")
        with out('try'):
            out('deserializeRequest(args, buf.get(), iprot.get(), c.get());')
        with out('catch (const std::exception& ex)'):
            if function.oneway:
                out('LOG(ERROR) << ex.what() << " in function noResponse";')
                out('return;')
            else:
                out('ProtocolOut_ prot;')
                self._generate_app_ex(service, 'ex.what()',
                                      function.name, "ctx->getProtoSeqId()",
                                      False, out, 'ctx',
                                      'PROTOCOL_ERROR')
        args = []
        for idx, member in enumerate(function.arglist.members):
            if self.flag_stack_arguments:
                args.append(self._get_pargs_field(idx, member))
            elif self._is_complex_type(member.type):
                args.append("std::move({0})".format(
                        aprefix + member.name))
            else:
                args.append(self._get_pargs_field(idx, member, False))
        if function.oneway:
            out('std::unique_ptr<apache::thrift::HandlerCallbackBase> callback(' +
              'new apache::thrift::HandlerCallbackBase(std::move(req), ' +
              'std::move(c), nullptr, eb, tm, ctx));')
        else:
            cb_class = self._get_handler_callback_class(function)
            out(('auto callback = std::make_unique<{0}>(std::move(req), ' +
               'std::move(c), return_{1}<ProtocolIn_,' +
               'ProtocolOut_>, throw_wrapped_{1}<ProtocolIn_,' +
               ' ProtocolOut_>, ctx->getProtoSeqId(),' +
               ' eb, tm, ctx);').format(cb_class, function.name))
        # Oneway request won't be canceled if expired. see D1006482 for
        # further details. TODO: fix this
        if not self._is_processed_in_eb(function) and not function.oneway:
            with out('if (!callback->isRequestActive())'):
                out('callback.release()->deleteInThread();')
                out('return;')
        args.insert(0, 'std::move(callback)')
        out('ctx->setStartedProcessing();')
        out('iface_->{0}({1});'.format(self._get_async_func_name(function),
                                     ", ".join(args)))

    def _generate_processor(self, service, s):
        if not service.extends:
            class_signature = 'class {0} : '.format(
                service.name + 'AsyncProcessor') + \
                'public ::apache::thrift::GeneratedAsyncProcessor'
        else:
            class_signature = 'class {0} : '.format(
                service.name + 'AsyncProcessor') + \
                'public ' + self._type_name(service.extends) + \
                'AsyncProcessor'
        with s.cls(class_signature):
            out().label('public:')
            with out().defn(
                    'const char* {name}()',
                    name='getServiceName',
                    override=True):
                out("return \"{0}\";".format(service.name))

            base_of = lambda n: "{}AsyncProcessor".format(self._type_name(n))
            base = base_of(service.extends) if service.extends else "void"
            out('using BaseAsyncProcessor = {};'.format(base))

            out('using HasFrozen2 = std::false_type;')

            out().label('protected:')

            out('{0}SvIf* iface_;'.format(service.name))
            with out().defn(
                    'folly::Optional<std::string> {name}(' +
                    'folly::IOBuf* buf, ' +
                    'apache::thrift::protocol::PROTOCOL_TYPES protType)',
                    name='getCacheKey',
                    override=True):
                out('return apache::thrift::detail::ap::get_cache_key(' +
                    'buf, protType, cacheKeyMap_);')

            out().label('public:')

            with out().defn(
                    'void {name}(std::unique_ptr<' +
                    'apache::thrift::ResponseChannel::Request> req, ' +
                    'std::unique_ptr<folly::IOBuf> buf, ' +
                    'apache::thrift::protocol::PROTOCOL_TYPES protType, ' +
                    'apache::thrift::Cpp2RequestContext* context, ' +
                    'folly::EventBase* eb, ' +
                    'apache::thrift::concurrency::ThreadManager* tm)',
                    name='process',
                    override=True):
                out('apache::thrift::detail::ap::process(' +
                    'this, std::move(req), std::move(buf), protType, ' +
                    'context, eb, tm);')

            out().label('protected:')

            with out().defn(
                    'bool {name}(const folly::IOBuf* buf, ' +
                    'const apache::thrift::transport::THeader* header)',
                    name='isOnewayMethod',
                    override=True):
                out('return apache::thrift::detail::ap::is_oneway_method(' +
                    'buf, header, onewayMethods_);')

            out().label('private:')
            oneways = out().defn('std::unordered_set<std::string> {name}',
                                 name='onewayMethods_',
                                 modifiers='static')
            oneways.epilogue = ';\n'
            with oneways:
                out(',\n'.join('"' + function.name + '"'
                        for function in service.functions if function.oneway))
            cache_map_type = 'std::unordered_map<std::string, int16_t>'
            cache_map = out().defn(cache_map_type + ' {name}',
                    name='cacheKeyMap_',
                    modifiers='static')
            cache_map.epilogue = ';'

            cache_member_map = {}
            for function in service.functions:
                cache_key = self._get_cache_key(function)
                if cache_key is not None:
                    if not cache_key.type.is_string:
                        raise CompilerError('Cache annotation is only \
                                allowed on string types')
                    cache_member_map[function.name] = cache_key.key
            with cache_map:
                out(',\n'.join("{{\"{}\", {}}}".format(function, cache_key)
                        for function, cache_key in
                        cache_member_map.iteritems()))
            prot = 0
            for shortprot, protname, prottype in self.protocols:
                if shortprot == 'simple_json':
                    continue
                out().label('public:')
                out((
                    'using {proto}ProcessFunc = ' +
                    'ProcessFunc<{klass}AsyncProcessor, ' +
                    'apache::thrift::{proto}Reader>;')
                    .format(proto=protname, klass=service.name))
                out('using {proto}ProcessMap = ProcessMap<{proto}ProcessFunc>;'
                    .format(proto=protname))
                map_type = '{klass}AsyncProcessor::{proto}ProcessMap' \
                    .format(proto=protname, klass=service.name)
                map_name = '{0}ProcessMap_'.format(shortprot)
                with out().defn('const ' + map_type + '& {name}()',
                                name='get{proto}ProcessMap'
                                    .format(proto=protname),
                                modifiers='static'):
                    out('return {proto}ProcessMap_;'.format(proto=shortprot))
                out().label('private:')
                process_map = out().defn('const ' + map_type + ' {name}',
                                         name=map_name,
                                         modifiers='static')
                if prot < 1:  # TODO: fix build tool to use more than 2 outputs
                    prot = prot + 1
                process_map.epilogue = ';\n\n'

                with process_map:
                    p = service.name + 'AsyncProcessor'
                    r = 'apache::thrift::{prot}Reader'.format(prot=protname)
                    w = 'apache::thrift::{prot}Writer'.format(prot=protname)
                    for function in service.functions:
                        n = function.name
                        f = self._get_handler_function_name(function)
                        fp = '&{p}::{f}<{r}, {w}>'.format(p=p, f=f, r=r, w=w)
                        out('{{"{n}", {fp}}},'.format(n=n, fp=fp))

            out().label('private:')
            for function in service.functions:
                if not self._is_processed_in_eb(function):
                    with out().defn(
                            'template <typename ProtocolIn_, '
                            'typename ProtocolOut_>\n'
                            'void {name}(std::unique_ptr<'
                            'apache::thrift::ResponseChannel::Request> req, '
                            'std::unique_ptr<folly::IOBuf> buf, '
                            'std::unique_ptr<ProtocolIn_> iprot, '
                            'apache::thrift::Cpp2RequestContext* ctx, '
                            'folly::EventBase* eb, '
                            'apache::thrift::concurrency::ThreadManager* tm'
                            ')',
                            name="_processInThread_{0}"
                                .format(function.name),
                            output=self._out_tcc):
                        out('auto pri = iface_->getRequestPriority(ctx, '
                            'apache::thrift::concurrency::{0});'.format(
                                self._get_function_priority(service, function)))
                        out('processInThread<ProtocolIn_, ProtocolOut_>' +
                          '(std::move(req), std::move(buf),' +
                          'std::move(iprot), ctx, eb, tm, pri, '
                          + (function.oneway and
                          'apache::thrift::RpcKind::SINGLE_REQUEST_NO_RESPONSE'
                          or 'apache::thrift::RpcKind::SINGLE_REQUEST_SINGLE_RESPONSE') +
                          ', &{0}AsyncProcessor::process_{1}'.format(
                                  service.name, function.name) +
                          '<ProtocolIn_, ProtocolOut_>, this);')

                with out().defn('template <typename ProtocolIn_, ' +
                            'typename ProtocolOut_>\n' +
                            'void {name}(std::unique_ptr<' +
                            'apache::thrift::ResponseChannel::Request> req, ' +
                            'std::unique_ptr<folly::IOBuf> buf, ' +
                            'std::unique_ptr<ProtocolIn_> iprot,' +
                            'apache::thrift::Cpp2RequestContext* ctx,' +
                            'folly::EventBase* eb, ' +
                            'apache::thrift::concurrency::ThreadManager* tm)',
                            name="process_{0}".format(function.name),
                            output=self._out_tcc):
                    self._generate_process_function(service, function)

                if not function.oneway:
                    args = [
                        'int32_t protoSeqId',
                        'apache::thrift::ContextStack* ctx']

                    if not function.returntype.is_void:
                        args.append("{0} const& _return".format(
                                self._type_name(function.returntype)))

                    with out().defn(
                        'template <class ProtocolIn_, class ProtocolOut_>\n' +
                                'folly::IOBufQueue {name}' + '({0})'
                                .format(", ".join(args)),
                                name="return_{0}".format(function.name),
                                output=self._out_tcc,
                                modifiers='static'):
                        out('ProtocolOut_ prot;')
                        out('{0}_{1}_presult result;'.format(
                                service.name, function.name))
                        if self._function_produces_result(function):
                            out('{0} = const_cast<{1}*>(&_return);'.format(
                                self._get_presult_success(),
                                self._type_name(function.returntype)))
                            out('{0};'.format(
                                self._get_presult_success_isset('true')))
                        out('return serializeResponse("{0}", '
                          '&prot, protoSeqId, ctx, result);'
                          .format(function.name))

                def cast_xceptions(xceptions):
                    for idx, xception in enumerate(xceptions):
                        with out('catch (const {0}& e)'.format(
                            self._type_name(xception.type))):
                            ew = 'folly::exception_wrapper(ep, e)'
                            out('ctx->userExceptionWrapped(true, {});'.format(ew))
                            ex_idx = self._exception_idx(function, idx)
                            out('{0} = e;'.format(
                                self._get_presult_exception(ex_idx, xception)))
                            out('{0};'.format(
                                self._get_presult_exception_isset(ex_idx, xception, 'true')))
                if not function.oneway:
                    with out().defn(
                        'template <class ProtocolIn_, class ProtocolOut_>\n' +
                        'void {name}(std::unique_ptr' +
                        '<apache::thrift::ResponseChannel::Request> req,' +
                        'int32_t protoSeqId,' +
                        'apache::thrift::ContextStack* ctx,' +
                        'folly::exception_wrapper ew,' +
                        'apache::thrift::Cpp2RequestContext* reqCtx)',
                        name="throw_wrapped_{0}".format(function.name),
                        modifiers='static',
                        output=self._out_tcc
                    ):
                        with out('if (!ew)'):
                            out('return;')

                        out('ProtocolOut_ prot;')
                        if len(function.xceptions.members) > 0:
                            out('{0}_{1}_presult result;'.format(
                                service.name, function.name))
                        for idx, xception in enumerate(function.xceptions.members):
                            xception_type = self._type_name(
                                xception.type)
                            with out('if (ew.with_exception([&]({0}& e)'.
                                     format(xception_type)):
                                out('ctx->userExceptionWrapped(true, ew);')
                                ex_idx = self._exception_idx(function, idx)
                                out('{0} = e;'.format(
                                    self._get_presult_exception(ex_idx, xception)))
                                out('{0};'.format(
                                    self._get_presult_exception_isset(ex_idx, xception, 'true')))
                            out(')) {} else ')
                        with out(' '):
                            self._generate_app_ex(
                                service,
                                'ew.what().toStdString()',
                                function.name, "protoSeqId", True,
                                out(), 'reqCtx', None,
                                uex_ew='ew')
                        if len(function.xceptions.members) > 0:
                            out('auto queue = serializeResponse('
                                '"{0}", &prot, protoSeqId, ctx,'
                                ' result);'.format(function.name))
                            out('queue.append('
                                'apache::thrift::transport::THeader::transform('
                                'queue.move(), '
                                '{0}->getHeader()->getWriteTransforms(), '
                                '{0}->getHeader()->getMinCompressBytes()));'.format('reqCtx'))
                            out('return req->sendReply(queue.move());')


            out().label('public:')
            init = OrderedDict()
            if service.extends:
                init[self._type_name(service.extends) + 'AsyncProcessor'] = \
                    'iface'
            init['iface_'] = 'iface'
            out().defn('{name}(' + service.name + 'SvIf* iface)',
                   name=service.name + 'AsyncProcessor',
                   init_dict=init,
                   in_header=True).scope.empty()
            out().defn('{name}()', name='~' + service.name + 'AsyncProcessor',
                   in_header=True, modifiers='virtual').scope.empty()

    def _get_handler_function_name(self, function):
        if self._is_processed_in_eb(function):
            return 'process_' + function.name
        else:
            return '_processInThread_' + function.name

    def _generate_client_sync_function(self, service, function,
                                       uses_rpc_options=False):

        signature = self._get_sync_function_signature(function,
                                                      uses_rpc_options)

        with out().defn(signature,
                name="sync_" + function.name,
                modifiers='virtual',
                output=self._additional_outputs[-1]):
            common_args = [arg.name for arg in function.arglist.members]

            if not uses_rpc_options:
                out('::apache::thrift::RpcOptions rpcOptions;')
                if function.returntype.is_void:
                    args = ["rpcOptions"]
                    args.extend(common_args)
                    args_list = ", ".join(args)
                    out("sync_{name}({args_list});".format(name=function.name,
                                                         args_list=args_list))
                elif not self._is_complex_type(function.returntype):
                    args = ["rpcOptions"]
                    args.extend(common_args)
                    args_list = ", ".join(args)

                    out("return sync_{name}({args_list});"
                         .format(name=function.name, args_list=args_list))
                else:
                    args = ["rpcOptions", "_return"]
                    args.extend(common_args)
                    args_list = ", ".join(args)

                    out("sync_{name}({args_list});"
                         .format(name=function.name, args_list=args_list))

            else:
                out('apache::thrift::ClientReceiveState _returnState;')

                out(
                    "auto callback = "
                    "std::make_unique<apache::thrift::ClientSyncCallback>("
                    "&_returnState, {isOneWay});".format(
                        isOneWay=(
                            "apache::thrift::RpcKind::SINGLE_REQUEST_NO_RESPONSE"
                            if function.oneway else
                            "apache::thrift::RpcKind::SINGLE_REQUEST_SINGLE_RESPONSE"
                        )
                    )
                )

                args = ["true", "rpcOptions", "std::move(callback)"]
                args.extend(common_args)
                args_list = ", ".join(args)

                out("{name}Impl({args_list});".format(name=function.name,
                                                      args_list=args_list))

                if not function.oneway:
                    out("SCOPE_EXIT {")
                    out("  if (_returnState.header() && "
                            "!_returnState.header()->getHeaders().empty()) {")
                    out("    rpcOptions.setReadHeaders("
                            "_returnState.header()->releaseHeaders());")
                    out("  }")
                    out("};")

                    with out("if (!_returnState.buf())"):
                        out("assert(_returnState.exception());")
                        out("_returnState.exception().throw_exception();")

                    if not function.returntype.is_void:
                        if not self._is_complex_type(function.returntype):
                            out("return recv_{}(_returnState);"
                                    .format(function.name))
                        else:
                            out("recv_{}(_return, _returnState);"
                                    .format(function.name))
                    else:
                        out("recv_{}(_returnState);".format(function.name))

    def _get_sync_function_signature(self, function, uses_rpc_options=False):
        params = []

        if uses_rpc_options:
            params.append("apache::thrift::RpcOptions& rpcOptions")

        if function.returntype.is_void:
            return_type = "void"
        elif not self._is_complex_type(function.returntype):
            return_type = self._type_name(function.returntype)
        else:
            return_type = "void"
            params.append(self._type_name(function.returntype) + "& _return")

        param_list = ", ".join(params)
        param_list += self._argument_list(function.arglist,
                                          add_comma=bool(params),
                                          unique=False)

        return return_type + " {name}(" + param_list + ")"

    def _generate_client_nonrpcoptions_function(self, service, function,
                                                function_name):
        out("::apache::thrift::RpcOptions rpcOptions;")
        args = ["rpcOptions"]

        args.extend([arg.name for arg in function.arglist.members])
        args_list = ", ".join(args)

        out("return {function}({args});"
              .format(function=function_name, args=args_list))

    def _generate_client_future_function(self, service, function,
                                         uses_rpc_options=False, header=False):

        if header:
            function_name = "header_future_" + function.name
        else:
            function_name = "future_" + function.name
        signature = self._get_future_function_signature(function,
                                                        uses_rpc_options,
                                                        header)
        with out().defn(signature,
                name=function_name,
                modifiers='virtual',
                output=self._additional_outputs[-1]):
            if not uses_rpc_options:
                self._generate_client_nonrpcoptions_function(service,
                        function, function_name)
            else:
                common_args = []
                for arg in function.arglist.members:
                    common_args.append(arg.name)

                return_type = _lift_unit(self._type_name(function.returntype))

                if header:
                    out("folly::Promise<std::pair<{type}, std::unique_ptr<apache::thrift::transport::THeader>>> _promise;"
                            .format(type=return_type))
                else:
                    out("folly::Promise<{type}> _promise;"
                            .format(type=return_type))

                out("auto _future = _promise.getFuture();")

                args = ["rpcOptions"]
                end_args = []

                if function.oneway:
                    out("auto callback = "
                        "std::make_unique<apache::thrift::OneWayFutureCallback>("
                        "std::move(_promise), channel_);")

                else:
                    if header:
                        future_cb_name = "HeaderFutureCallback"
                    else:
                        future_cb_name = "FutureCallback"
                    out("auto callback = "
                        "std::make_unique<apache::thrift::{future_cb}<{type}>>("
                        "std::move(_promise), recv_wrapped_{name}, channel_);"
                        .format(future_cb=future_cb_name,
                                type=return_type,
                                name=function.name))

                args.append("std::move(callback)")
                args.extend(common_args)
                args.extend(end_args)
                args_list = ", ".join(args)

                out("{name}({args_list});".format(name=function.name,
                                                args_list=args_list))

                out("return _future;")

    def _generate_client_streaming_function(self, service, function,
                                            uses_rpc_options=False):

        function_name = "observable_" + function.name
        signature = self._get_streaming_function_signature(function,
                                                           uses_rpc_options)
        with out().defn(signature, name=function_name,
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
            if not uses_rpc_options:
                self._generate_client_nonrpcoptions_function(service,
                        function, function_name)
            else:
                args = [arg.name for arg in function.arglist.members]
                arglist = ", ".join(args)

                return_type = self._type_name(function.returntype)

                subj = self.tmp("subj")
                out("auto {subj} = std::make_shared<wangle::Subject<{type}>>();".format(
                    type=return_type, subj=subj))
                out("{name}(rpcOptions, "
                        "std::unique_ptr<apache::thrift::RequestCallback>("
                            "new apache::thrift::FunctionReplyCallback("
                            "[{subj}](apache::thrift::ClientReceiveState&& state) mutable {{ "
                        "apache::thrift::clientCallbackToObservable("
                            "state, recv_wrapped_{name}, {subj}); "
                        "}})), {args});".format(
                            name=function.name, subj=subj, args=arglist))
                out("return {subj};".format(subj=subj))

    def _get_streaming_function_signature(self, function, uses_rpc_options):
        result_type = self._type_name(function.returntype)
        return self._get_noncallback_function_signature(
                function,
                uses_rpc_options,
                "wangle::ObservablePtr",
                result_type)

    def _get_future_function_signature(self, function, uses_rpc_options, header):
        result_type = _lift_unit(self._type_name(function.returntype))
        if header:
            result_type = "std::pair<{0}, std::unique_ptr<apache::thrift::transport::THeader>>".format(result_type)
        return self._get_noncallback_function_signature(
                function, uses_rpc_options, "folly::Future", result_type)

    def _get_noncallback_function_signature(
            self, function, uses_rpc_options, ret_template, result_type):
        params = []
        if uses_rpc_options:
            params.append("apache::thrift::RpcOptions& rpcOptions")

        return_type = "{0}<{1}>".format(ret_template, result_type)

        param_list = ", ".join(params)
        param_list += self._argument_list(function.arglist,
                                          add_comma=bool(params),
                                          unique=False)

        return return_type + " {name}(" + param_list + ")"

    def _generate_client_async_function(self, service, function,
                                        uses_rpc_options=False,
                                        uses_sync=False):
        if not uses_rpc_options:
            signature = self._get_async_function_signature(
                    function, uses_rpc_options=False, uses_sync=False)
            with out().defn(signature,
                    name=function.name,
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
                out('::apache::thrift::RpcOptions rpcOptions;')
                args = ["false", "rpcOptions", "std::move(callback)"]

                args.extend([arg.name for arg in function.arglist.members])
                args_list = ", ".join(args)

                out("{name}Impl({args});".format(
                        name=function.name, args=args_list))

        elif not uses_sync:
            signature = self._get_async_function_signature(
                    function, uses_rpc_options=True, uses_sync=False)
            with out().defn(signature,
                    name=function.name,
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
                args = ["false", "rpcOptions", "std::move(callback)"]

                args.extend([arg.name for arg in function.arglist.members])
                args_list = ", ".join(args)

                out("{name}Impl({args});".format(
                        name=function.name, args=args_list))

        else:
            signature = self._get_async_function_signature(
                    function, uses_rpc_options=True, uses_sync=True,
                    uses_callback_ptr=True)

            out().label('private:')
            with out().defn(signature,
                    name=function.name + "Impl",
                    modifiers='virtual',
                    output=self._additional_outputs[-1]):
                args = ["&writer", "useSync", "rpcOptions"]
                args.append("std::move(callback)")

                args.extend([arg.name for arg in function.arglist.members])
                args_list = ", ".join(args)

                with out("switch(getChannel()->getProtocolId())"):
                    for key, val, prot in self.protocols:
                        if key == 'simple_json':
                            continue
                        with out().case("apache::thrift::protocol::" + prot):
                            out("apache::thrift::{0}Writer writer;".format(val))
                            out("{name}T({args});".
                              format(name=function.name, args=args_list))

                    with out().case("default", nobreak=True):
                        out("apache::thrift::detail::ac::throw_app_exn("
                            '"Could not find Protocol");')
            out().label('public:')

    def _generate_templated_client_function(self, service, function):
        signature = self._get_async_function_signature(function,
                                                       uses_rpc_options=True,
                                                       uses_sync=True,
                                                       uses_template=True,
                                                       uses_callback_ptr=True)

        func_name = function.name + "T"

        with out().defn(signature, name=func_name, output=self._out_tcc):
            out("auto header = std::make_shared<apache::thrift::transport::THeader>(apache::thrift::transport::THeader::ALLOW_BIG_FRAMES);")
            out("header->setProtocolId(getChannel()->getProtocolId());")
            out("header->setHeaders(rpcOptions.releaseWriteHeaders());")
            out("connectionContext_->setRequestHeader(header.get());")
            out("std::unique_ptr<apache::thrift::ContextStack> ctx = "
              "this->getContextStack(this->getServiceName(), "
              '"{0}.{1}", connectionContext_.get());'
              .format(service.name, function.name))
            pargs_class = "{0}_{1}_pargs".format(service.name, function.name)
            out("{0} args;".format(pargs_class))

            # Generate list of function args
            for idx, field in enumerate(function.arglist.members):
                rtype = self._get_true_type(field.type)
                if rtype.is_string or rtype.is_container \
                        or rtype.is_struct:
                    out("{0} = const_cast<{1}*>(&{2});".format(
                            self._get_pargs_field(idx, field),
                            self._type_name(field.type),
                            field.name))
                else:
                    out("{0} = &{1};".format(
                        self._get_pargs_field(idx, field),
                        field.name))

            sizer = 'auto sizer = [&](Protocol_* p) ' \
                    '{{ return args.serializedSizeZC(p); }};'
            writer = 'auto writer = [&](Protocol_* p) ' \
                     '{{ args.write(p); }};'
            out(sizer.format(pargs_class))
            out(writer.format(pargs_class))

            out('apache::thrift::clientSendT<Protocol_>(prot, rpcOptions, '
                'std::move(callback), std::move(ctx), header, '
                'channel_.get(), "{1}", writer, sizer, {2}, '
                'useSync);'.format(pargs_class,
                                     function.name,
                                     ["false", "true"][function.oneway]))
            out("connectionContext_->setRequestHeader(nullptr);")

    def _get_async_function_signature(self,
                                      function,
                                      uses_rpc_options,
                                      uses_template=False,
                                      uses_callback_ptr=False,
                                      uses_sync=True):
        signature_prefix = ""
        if uses_template:
            signature_prefix = "template <typename Protocol_>\n"

        params = []
        if uses_template:
            params.append("Protocol_* prot")

        if uses_sync:
            params.append("bool useSync")

        if uses_rpc_options:
            params.append("apache::thrift::RpcOptions& rpcOptions")

        params.append("std::unique_ptr<apache::thrift::RequestCallback> "
                      "callback")

        param_list = ", ".join(params)

        param_list += self._argument_list(function.arglist,
                                          add_comma=bool(params),
                                          unique=False)
        return signature_prefix + "void {name}(" + param_list + ")"

    def _generate_client_folly_function(self, function, name_prefix=""):
        sig = ("void {name}(folly::Function<void ("
               "::apache::thrift::ClientReceiveState&&)> callback" +
               self._argument_list(function.arglist, True, unique=False) + ")")

        args = ["std::make_unique<apache::thrift::FunctionReplyCallback>("
                "std::move(callback))"]
        args.extend([arg.name for arg in function.arglist.members])
        args_list = ", ".join(args)

        name = name_prefix + function.name

        with out().defn(sig,
                        name=name,
                        modifiers="virtual",
                        output=self._additional_outputs[-1]):
            out("{name}({args});".format(name=function.name, args=args_list))

    def _generate_throwing_recv_function(self, function, uses_template):
        callee_name = function.name
        if uses_template:
            callee_name = callee_name + 'T'
        output = None
        if uses_template:
            output = self._out_tcc
        else:
            output = self._additional_outputs[-1]
        with out().defn(self._get_recv_function_signature(function,
                                                          uses_template),
                        name='recv_' + callee_name,
                        modifiers='static',
                        output=output):
            simple_return = not function.returntype.is_void and \
                not self._is_complex_type(function.returntype)
            if simple_return:
                out(self._type_name(function.returntype) + ' _return;')

            params = []
            if uses_template:
                params.append('prot')
            if not function.returntype.is_void:
                params.append('_return')
            params.append('state')
            func_name = 'recv_wrapped_' + function.name
            if uses_template:
                func_name = func_name + 'T'
            out('auto ew = {0}({1});'.format(func_name, ', '.join(params)))
            with out('if (ew)'):
                out('ew.throw_exception();')
            if simple_return:
                out('return _return;')

    def _generate_recv_functions(self, function):
        with out().defn(self._get_recv_function_signature(function,
                                                          is_wrapped=True),
                        name="recv_wrapped_" + function.name,
                        modifiers="static",
                        output=self._additional_outputs[-1]):
            with out('if (state.isException())'):
                out('return std::move(state.exception());')
            with out('if (!state.buf())'):
                out('return folly::make_exception_wrapper<'
                    'apache::thrift::TApplicationException>('
                    '"recv_ called without result");')
            with out("switch(state.protocolId())"):
                for key, value, prottype in self.protocols:
                    if key == 'simple_json':
                        continue
                    with out().case('apache::thrift::protocol::' + prottype,
                                    nobreak=True):
                        out("apache::thrift::{0}Reader reader;".format(value))

                        args = ["&reader"]

                        if not function.returntype.is_void:
                            args.append("_return")

                        args.append("state")
                        args_list = ", ".join(args)

                        out('return recv_wrapped_' + function.name + 'T(' +
                            args_list + ');')

                with out().case("default", nobreak=True):
                    pass
            out('return folly::make_exception_wrapper<'
                'apache::thrift::TApplicationException>('
                '"Could not find Protocol");')

        self._generate_throwing_recv_function(function, False)

        # Most mock frameworks require your functions to be virtual instance
        # functions. Generating a virtual instance version of recv_ so
        # that folks wanting to mock the thrift service clients can use
        # this function and override in their mock objects.
        out("// Mock friendly virtual instance method")
        with out().defn(self._get_recv_function_signature(function),
                    name="recv_" + "instance_" + function.name,
                    modifiers="virtual",
                    output=self._additional_outputs[-1]):
            params = []
            if self._is_complex_type(function.returntype):
                params.append('_return')
            params.append('state')
            if not function.oneway:
                if not function.returntype.is_void:
                    out("return recv_" + function.name +
                        "(" + ", ".join(params) + ");")
                else:
                    out("recv_" + function.name + "(" + ", ".join(params) +
                        ");")

        with out().defn(self._get_recv_function_signature(function,
                                                          is_wrapped=True),
                    name="recv_instance_wrapped_" + function.name,
                    modifiers="virtual",
                    output=self._additional_outputs[-1]):
            params = []
            if not function.returntype.is_void:
                params.append('_return')
            params.append('state')
            if not function.oneway:
                out("return recv_wrapped_" + function.name +
                    "(" + ", ".join(params) + ");")

    def _generate_templated_recv_function(self, service, function):
        sig = self._get_recv_function_signature(function,
                                                uses_template=True,
                                                is_wrapped=True)

        with out().defn(sig,
                    name="recv_wrapped_" + function.name + "T",
                    modifiers="static",
                    output=self._out_tcc):
            with out('if (state.isException())'):
                out('return std::move(state.exception());')
            out("prot->setInput(state.buf());")
            out("auto guard = folly::makeGuard([&] {prot->setInput(nullptr);});")
            out("apache::thrift::ContextStack* ctx = state.ctx();")
            out("std::string _fname;")
            out("int32_t protoSeqId = 0;")
            out("apache::thrift::MessageType mtype;")
            out("ctx->preRead();")

            out("folly::exception_wrapper interior_ew;")
            with out("auto caught_ew = folly::try_and_catch<"
                     "std::exception, "
                     "apache::thrift::TException, "
                     "apache::thrift::protocol::TProtocolException>([&]() "):
                out("prot->readMessageBegin(_fname, mtype, protoSeqId);")

                with out("if (mtype == apache::thrift::T_EXCEPTION)"):
                    out("apache::thrift::TApplicationException x;")
                    out("x.read(prot);")
                    out("prot->readMessageEnd();")
                    out("interior_ew = folly::make_exception_wrapper<"
                        "apache::thrift::TApplicationException>(x);")
                    out("return; // from try_and_catch")

                with out("if (mtype != apache::thrift::T_REPLY)"):
                    out("prot->skip(apache::thrift::protocol::T_STRUCT);")
                    out("prot->readMessageEnd();")
                    out("interior_ew = folly::make_exception_wrapper<"
                        "apache::thrift::TApplicationException>("
                        "apache::thrift::TApplicationException"
                        "::TApplicationExceptionType::INVALID_MESSAGE_TYPE);")
                    out("return; // from try_and_catch")

                with out('if (_fname.compare("' + function.name + '") != 0)'):
                    out("prot->skip(apache::thrift::protocol::T_STRUCT);")
                    out("prot->readMessageEnd();")
                    out("interior_ew = folly::make_exception_wrapper<"
                        "apache::thrift::TApplicationException>("
                        "apache::thrift::TApplicationException"
                        "::TApplicationExceptionType::WRONG_METHOD_NAME);")
                    out("return; // from try_and_catch")
                out("::apache::thrift::SerializedMessage smsg;")
                out("smsg.protocolType = prot->protocolType();")
                out("smsg.buffer = state.buf();")
                out("ctx->onReadData(smsg);")

                out("{0}_{1}_presult result;".format(service.name, function.name))

                if not function.returntype.is_void:
                    out("{0} = &_return;".format(
                        self._get_presult_success()))

                out("result.read(prot);")

                out("prot->readMessageEnd();")
                out('ctx->postRead(state.header(), state.buf()->length());')

                if not function.returntype.is_void:
                    with out("if ({0})".format(self._get_presult_success_isset())):
                        out("// _return pointer has been filled")
                        out("return; // from try_and_catch")
                for idx, xs in enumerate(function.xceptions.members):
                    ex_idx = self._exception_idx(function, idx)
                    with out("if ({0})".format(
                            self._get_presult_exception_isset(ex_idx, xs))):
                        out("interior_ew = folly::make_exception_wrapper<"
                                "{0}>({1});".format(
                                self._type_name(xs.type),
                                self._get_presult_exception(ex_idx, xs)))
                        out("return; // from try_and_catch")

                if not function.returntype.is_void:
                    with out("else"):
                        # else throw, no success
                        out("interior_ew = folly::make_exception_wrapper<"
                            "apache::thrift::TApplicationException>("
                            "apache::thrift::TApplicationException::"
                            "TApplicationExceptionType::MISSING_RESULT, "
                            '"failed: unknown result");')
                        out("return; // from try_and_catch")
            out(");")
            out("auto ew = interior_ew ? std::move(interior_ew) : std::move(caught_ew);")
            with out("if (ew)"):
                out("ctx->handlerErrorWrapped(ew);")
            out("return ew;")

        self._generate_throwing_recv_function(function, True)

    def _get_recv_function_signature(self, function, uses_template=False,
                                     is_wrapped=False):
        signature_prefix = ""

        if uses_template:
            signature_prefix = "template <typename Protocol_>\n"

        if is_wrapped:
            signature_prefix += 'folly::exception_wrapper'
        elif function.returntype.is_void or \
           self._is_complex_type(function.returntype):
            signature_prefix += "void"
        else:
            signature_prefix += self._type_name(function.returntype)

        params = []
        if uses_template:
            params.append("Protocol_* prot")

        if not function.returntype.is_void and (
            self._is_complex_type(function.returntype) or is_wrapped
        ):
            params.append(self._type_name(function.returntype) + "& _return")

        params.append("::apache::thrift::ClientReceiveState& state")
        param_list = ", ".join(params)
        return signature_prefix + " {name}(" + param_list + ")"

    def _recv_has_void_return_type(self, function):
        return not self._function_produces_result(function) or \
               self._recv_uses_return_parameter(function)

    def _recv_uses_return_parameter(self, function):
        return self._function_produces_result(function) and \
               self._is_complex_type(function.returntype)

    def _function_produces_result(self, function):
        return_type = function.returntype
        return not return_type.is_void

    def _exception_idx(self, function, idx):
        return idx + (1 if self._function_produces_result(function) else 0)

    def _argument_list(self, arglist, add_comma, unique, no_param_names=False):
        out = ""
        for field in arglist.members:
            if len(out) > 0 or add_comma:
                out += ", "

            type_name = self._type_name(field.type,
                                        arg=True, unique=unique)

            field_name = "/*" + field.name + "*/" if no_param_names \
                         else field.name
            out += type_name + " " + field_name

        return out

    # ======================================================================
    # STRUCT GENERATION CODE + following two sections
    # ======================================================================

    def _default_value(self, t):
        t = self._get_true_type(t)
        if t.is_base_type or t.is_enum:
            dval = None
            if t.is_enum:
                dval = "({0})0".format(self._type_name(t))
            elif t.is_string:
                dval = '{0}()'.format(self._type_name(t))
            else:
                dval = '0'
            return dval
        else:
            return False

    def _should_generate_field(self, field):
        return ('format' not in field.annotations or
                field.annotations['format'] != 'serialized')

    def _member_default_value(self, member, explicit=False):
        t = self._get_true_type(member.type)
        rt = self._cpp_ref_type(member, self._type_name(t))
        if member.value:
            return self._render_const_value(
                t, member.value, defining=member, allow_references=False)
        if self._is_optional_wrapped(member):
            return ''
        if t.is_base_type and not t.is_string:
            return '0'
        if explicit or t.is_enum:
            return self._render_const_value(
                t, member.value, defining=member, allow_references=False)
        if t.is_container and rt:
            tn = self._type_name(t)
            rann = (
                (self._cpp_annotation(member, 'ref') and 'unique') or
                self._cpp_annotation(member, 'ref_type'))
            rkey = {
                'unique': 'std::make_unique',
                'shared': 'std::make_shared',
                'shared_const': 'std::make_shared',
            }.get(rann)
            if rkey:
                return '{rkey}<{tn}>()'.format(rkey=rkey, tn=tn)
            return '{rt}(new {tn}())'.format(rt=rt, tn=tn)
        return ''

    def _get_serialized_fields_options(self, obj):
        keep_unknown_fields = ('keep_unknown_fields' in obj.annotations and
                               obj.annotations['keep_unknown_fields'] == '1')
        return SerializedFieldOptions(
            keep_unknown_fields=keep_unknown_fields,
            has_serialized_fields=keep_unknown_fields or any(
                    'format' in field.annotations and
                    field.annotations['format'] == 'serialized'
                    for field in obj.members)
        )

    def _gen_union_constructor(self, s, obj, is_operator, is_move):
        i = OrderedDict()
        if is_operator:
            if is_move:
                sig = '{name}& operator=({name}&& rhs)'
            else:
                sig = '{name}& operator=(const {name}& rhs)'
        else:
            i['type_'] = 'Type::__EMPTY__'
            if is_move:
                sig = '{name}({name}&& rhs)'
            else:
                sig = '{name}(const {name}& rhs)'

        with s.defn(sig, name=obj.name, in_header=True, init_dict=i):
            out('if (this == &rhs) {{return{0}; }}'.format(
                ' *this' if is_operator else ''))
            if is_operator:
                out('__clear();')
            out('if (rhs.type_ == Type::__EMPTY__) {{ return{0}; }}'.format(
                ' *this' if is_operator else ''))

            with out('switch(rhs.type_)'):
                for member in obj.members:
                    with out().case('Type::' + member.name):
                        if is_move:
                            if self._is_reference(member):
                                fmt = 'set_{field}(std::move(*rhs.value_.{field}));'
                            else:
                                fmt = 'set_{field}(std::move(rhs.value_.{field}));'
                        elif self._is_reference(member):
                            fmt = 'set_{field}(*rhs.value_.{field});'
                        else:
                            fmt = 'set_{field}(rhs.value_.{field});'
                        out(fmt.format(field=member.name))
                with out().case('default'):
                    out('assert(false);')

            if is_move:
                out('rhs.__clear();')
            if is_operator:
                out('return *this;')

    def _gen_union_switch(self, members, stmt, stmt_ref,
                          val='type_', default='assert(false);', nobreak=False):
        with out('switch({0})'.format(val)):
            for member in members:
                with out().case('Type::' + member.name, nobreak=nobreak):
                    if self._is_reference(member):
                        out(stmt_ref.format(field=member.name))
                    else:
                        out(stmt.format(field=member.name))
            with out().case('default', nobreak=nobreak):
                out(default)

    def _generate_struct_complete(self, s, obj, is_exception,
                                  pointers, read, write, swap,
                                  result, has_isset=True,
                                  to_additional=False, simplejson=True,
                                  is_service_helper=False):
        self._generated_types.append(obj)

        def output(data):
            if to_additional:
                print >>self._additional_outputs[-1], data + ';'
            elif is_service_helper:
                s.impl(data)
            else:
                # this writes both the extern, and the explicit instantiation
                # of the template to the .h and .cpp file, respectively
                s.extern(data)

        def check_if_deprecated(typename, obj_name, annotations):
            deprecated = ''
            if 'deprecated' in annotations:
                deprecated += '[[deprecated(\n  "'
                # For annotations that don't specify a value to the
                # 'key: "value"' pair, Thrift assigns the string "1"
                # as a default value.
                if annotations['deprecated'] != "1":
                    deprecated += annotations['deprecated']
                else:
                    deprecated += \
                        '{0} {1} is deprecated'.format(typename, obj_name)
                deprecated += '"\n)]] '
            return(deprecated)

        # generate explicit and extern template instantiations for
        # {Binary,Compact}Protocol
        def write_extern_templates():
            if not self.flag_implicit_templates:
                for a,b,c in self.protocols:
                    if a == 'simple_json' and not simplejson:
                        continue
                    output(("template uint32_t {1}::read<>"
                            "(apache::thrift::{0}Reader*)").format(
                                    b, obj.name))

                    output(("template uint32_t {1}::write<>"
                            "(apache::thrift::{0}Writer*) const").format(
                                    b, obj.name))
                    output(("template uint32_t {1}::serializedSize<>"
                            "(apache::thrift::{0}Writer const*)"
                            " const").format(b, obj.name))
                    output(("template uint32_t {1}::serializedSizeZC<>"
                            "(apache::thrift::{0}Writer const*) const").
                                format(b, obj.name))

        is_virtual = self._has_cpp_annotation(obj, 'virtual')
        extends = [
            'private apache::thrift::detail::st::ComparisonOperators<{0}>'
            .format(obj.name),
        ]
        if is_exception:
            extends += ['public apache::thrift::TException']
        extends = ' : ' + ', '.join(extends)
        # Open struct def
        class_signature = 'class '

        deprecated = check_if_deprecated('class', obj.name, obj.annotations)
        class_signature += deprecated + obj.name
        if not is_virtual:
            class_signature += ' final'
        class_signature += extends
        struct = s.cls(class_signature).scope
        struct.acquire()
        struct.label('public:')
        # Get members
        members = filter(self._should_generate_field, obj.members)
        has_nonrequired_fields = any(member.req != e_req.required
                                        for member in members)
        should_generate_isset = has_nonrequired_fields and \
            ((not pointers) or read) and not obj.is_union \
            and not self.flag_optionals
        struct_options = self._get_serialized_fields_options(obj)

        def is_heap_allocated(member):
            ttype = self._get_true_type(member.type)
            return self._is_reference(member) or \
                ttype.is_container or \
                (ttype.is_base_type and
                 ttype.as_base_type.base == frontend.t_base.string)

        # Outline constructors and destructors if the struct has
        # enough members and at least one has a non-trivial destructor
        # (involving at least a branch and a likely deallocation).
        # TODO(ott): Support unions.
        is_large = (not obj.is_union and
                    len(members) > 4 and
                    any(is_heap_allocated(member) for member in members))

        # Type enum for unions
        if obj.is_union:
            with struct('enum Type'):
                out('__EMPTY__ = 0,')
                for member in members:
                    out('{0} = {1},'.format(member.name, member.key))
            struct.sameLine(';')

        if not pointers:
            # Default constructor
            i = OrderedDict()
            if not obj.is_union:
                for member in members:
                    value = self._member_default_value(member)
                    if value:
                        i[member.name] = value
                struct()
            else:
                i['type_'] = 'Type::__EMPTY__'
            c = struct.defn('{name}()', name=obj.name,
                            in_header=not is_large,
                            init_dict=i).scope.empty()

            # generate from-string ctors for exceptions with message annotation
            if is_exception and 'message' in obj.annotations:
                i = OrderedDict()
                i[obj.annotations['message']] = '__message'
                c = struct.defn('explicit {name}(const std::string& __message)',
                                name=obj.name,
                                in_header=True,
                                init_dict=i).scope.empty()

                i = OrderedDict()
                i[obj.annotations['message']] = 'std::move(__message)'
                c = struct.defn('explicit {name}(std::string&& __message)',
                                name=obj.name,
                                in_header=True,
                                init_dict=i).scope.empty()

            is_copyable = self._is_copyable_struct(obj)
            is_comparable = self._is_comparable_struct(obj)
            if not obj.is_union:
                # BEGIN FRAGILE CONSTRUCTOR
                # Generate a initializer_list type constructor
                init_vars = []
                init_vars.append('apache::thrift::FragileConstructor')
                for member in members:
                    t = self._get_true_type(member.type)
                    typename = self._field_type_name(member, member.type)
                    init_vars.append("{0} {1}__arg".format(
                        typename, member.name))
                i = OrderedDict()
                for member in members:
                    i[member.name] = 'std::move({name}__arg)'.format(
                        name=member.name)
                struct('// FragileConstructor for use in'
                       ' initialization lists only')
                with struct.defn('{name}(' + ', '.join(init_vars) + ')',
                                 name=obj.name,
                                 in_header=False,
                                 init_dict=i).scope as c:
                    for member in members:
                        if self._has_isset(member) and not self.flag_optionals:
                            c("__isset.{0} = true;".format(member.name))
                # END FRAGILE CONSTRUCTOR

                # SELECTIVE CONSTRUCTOR
                for member in members:
                    if self._has_cpp_annotation(member, 'ref'):
                        ref_type = 'unique'
                    else:
                        ref_type = self._cpp_annotation(member, 'ref_type')
                    decayed = (
                        'folly::_t<std::decay<T__ThriftWrappedArgument__Ctor>>')
                    struct('template <typename T__ThriftWrappedArgument__Ctor, '
                        'typename... Args__ThriftWrappedArgument__Ctor>')
                    struct(('{0}(::apache::thrift::detail::argument_wrapper<'
                        '{1}, T__ThriftWrappedArgument__Ctor> arg, '
                        'Args__ThriftWrappedArgument__Ctor&&... args):').format(
                            obj.name, member.key))
                    struct(('  {0}(std::forward<'
                        'Args__ThriftWrappedArgument__Ctor>(args)...)').format(
                            obj.name))
                    struct('{')
                    if not ref_type:
                        arg = 'arg.move()'
                    elif ref_type == 'unique':
                        arg = 'std::make_unique<{0}>(arg.move())'.format(
                            decayed)
                    elif ref_type == 'shared' or ref_type == 'shared_const':
                        arg = 'std::make_shared<{0}>(arg.move())'.format(
                            decayed)
                    else:
                        arg = '{0}<{1}>(new {1}(arg.move()))'.format(
                            ref_type, decayed)
                    struct('  {0} = {1};'.format(member.name, arg))
                    if self._has_isset(member) and not self.flag_optionals:
                        struct('  __isset.{0} = true;'.format(member.name))
                    struct('}')
                # SELECTIVE CONSTRUCTOR

                # move constructor, move assignment, defaulted
                # (not implicitly declared because we have a destructor)
                if self._is_noex_move_ctor_struct(obj):
                    i = OrderedDict()
                    for member in members:
                        i[member.name] = 'std::move(other.{name})'.format(
                            name=member.name)
                    if should_generate_isset:
                        i['__isset'] = 'other.__isset'
                    c = struct.defn('{name}({unqualified_name}&& other)',
                                    name=obj.name,
                                    in_header=not is_large,
                                    no_except=True,
                                    init_dict=i).scope.empty()
                else:
                    c = struct.defn('{name}({unqualified_name}&&)',
                                    name=obj.name, in_header=True, default=True)
                if is_copyable:
                    needs_copy_constructor = False
                    for member in members:
                        if self._apply_unique_ptr_hack(member):
                            needs_copy_constructor = True
                    if needs_copy_constructor:
                        with struct.defn('{{name}}(const {0}& src)'
                                         .format(obj.name),
                                         name=obj.name):
                            for member in members:
                                # custom hacky logic for unique_ptr copying
                                # it would be much better to use a
                                # copyable wrapper instead
                                if self._apply_unique_ptr_hack(member):
                                    out("if (src.{0}) {0}.reset("
                                        "new {1}(*src.{0}));".format(
                                        member.name, self._type_name(
                                            member.type)))
                                else:
                                    out("{0} = src.{0};".format(
                                        member.name))
                                if self._has_isset(member):
                                    out('__isset.{0} = src.__isset.{0};'.format(
                                        member.name))
                    else:
                        c = struct.defn(
                            '{name}(const {name}&)',
                            name=obj.name, in_header=True, default=True)
                c = struct.defn('{name}& operator=({name}&&)',
                                name=obj.name, in_header=True, default=True)
                if is_copyable:
                    if needs_copy_constructor:
                        with struct.defn('{0}& {{name}}(const {0}& src)'
                                         .format(obj.name),
                                         name='operator='):
                            out('{name} tmp(src);'.format(
                                name=obj.name))
                            out('swap(*this, tmp);')
                            out('return *this;')
                    else:
                        c = struct.defn('{name}& operator=(const {name}&)',
                                    name=obj.name, in_header=True, default=True)

            else:
                # unions need to define the above constructors because of the
                # union member
                for op in False, True:
                    self._gen_union_constructor(struct, obj, op, True)
                    if is_copyable:
                        self._gen_union_constructor(struct, obj, op, False)

                # SELECTIVE CONSTRUCTOR
                for member in members:
                    struct('template <typename T__ThriftWrappedArgument__Ctor>')
                    struct(('{0}(::apache::thrift::detail::argument_wrapper<'
                        '{1}, T__ThriftWrappedArgument__Ctor> arg):').format(
                            obj.name, member.key))
                    struct('  type_(Type::__EMPTY__)')
                    struct('{')
                    struct('  set_{0}(arg.move());'.format(member.name))
                    struct('}')
                # SELECTIVE CONSTRUCTOR

            with struct.defn('void {name}()', name="__clear"):
                out('// clear all fields')
                if obj.is_union:
                    out('if (type_ == Type::__EMPTY__) { return; }')
                    self._gen_union_switch(members,
                        'destruct(value_.{field});',
                        'destruct(value_.{field});')
                    out('type_ = Type::__EMPTY__;')
                else:
                    for member in members:
                        t = self._get_true_type(member.type)
                        name = member.name + \
                            self._type_access_suffix(member.type)
                        if self._is_optional_wrapped(member):
                            # trumps below conditions, regardless of type
                            out(('{0}.clear();').format(name))
                        elif t.is_base_type or t.is_enum:
                            dval = self._member_default_value(
                                    member, explicit=True)
                            out('{0} = {1};'.format(name, dval))
                        elif t.is_struct or t.is_xception:
                            if t.members:
                                if self._is_reference(member):
                                    if self._is_const_shared_ptr(member):
                                        out('{0}.reset();'.format(name))
                                    else:
                                        out(('if ({1}) ::apache::thrift' +
                                             '::Cpp2Ops< {0}>' +
                                             '::clear({1}.get());').format(
                                                 self._type_name(
                                                     member.type),
                                                 name))
                                else:
                                    out(('::apache::thrift::Cpp2Ops< {0}>' +
                                         '::clear(&{1});').format(
                                             self._type_name(member.type),
                                                             name))
                        elif t.is_container:
                            if self._is_reference(member):
                                out('{0}.reset(new typename decltype({0})'
                                    '::element_type());'.format(name))
                            else:
                                out('{0}.clear();'.format(name))
                        else:
                            raise TypeError('Unknown type for member:' +
                                            member.name)
                    if should_generate_isset:
                        out('__isset = {};')
                    if struct_options.has_serialized_fields:
                        out('{0}.reset();'.format(
                            self._serialized_fields_name))
        # END if not pointers

        if is_virtual or is_large:
            with struct.defn('{name}()',
                             name='~' + obj.name,
                             modifiers='virtual' if is_virtual else '',
                             in_header=not is_large):
                if obj.is_union:
                    out('__clear();')
            struct()
        elif obj.is_union:
            with struct.defn('{name}()', name='~' + obj.name,
                             in_header=True):
                out('__clear();')

        s1 = struct
        if obj.is_union:
            s1 = struct('union storage_type').scope
            s1.acquire()

        # Declare all fields.
        for member in members:
            t = self._get_true_type(member.type)
            typename = self._field_type_name(member, member.type)
            deprecated = check_if_deprecated(
                typename, member.name, member.annotations)
            s1(self._declare_field(
                member,
                pointers and not member.type.is_xception,
                not read, False,
                self._is_optional_wrapped(member), deprecated))

        if s1 is not struct:
            s1()
            s1('storage_type() {}')
            s1('~storage_type() {}')
            s1.release()
            struct.sameLine(';')

        # Isset struct has boolean fields, but only for non-required fields
        if should_generate_isset:
            struct()
            with struct.cls('struct __isset', epilogue=' __isset = {};') as ist:
                # Declare boolean fields
                ist()
                for member in members:
                    if self._has_isset(member):
                        ist('bool {0};'.format(member.name))
        if struct_options.has_serialized_fields:
            struct()
            struct('apache::thrift::ProtocolType {0};'.format(
                       self._serialized_fields_protocol_name))
            struct('{0} {1};'.format(self._serialized_fields_type,
                                     self._serialized_fields_name))
        if ((not pointers and
             is_comparable and
             not struct_options.has_serialized_fields)):
            # Generate an equality testing operator.
            with struct.defn('bool {{name}}(const {0}& {1}) const'
                             .format(obj.name,
                                len(members) > 0 and 'rhs' or '/* rhs */'),
                             name='operator=='):
                if obj.is_union:
                    out('if (type_ != rhs.type_) { return false; }')
                    self._gen_union_switch(members,
                        'return value_.{field} == rhs.value_.{field};',
                        'return *value_.{field} == *rhs.value_.{field};',
                        default='return true;', nobreak=True)
                else:
                    for m in members:
                        # Most existing Thrift code does not use isset or
                        # optional/required, so we treat "default" fields as
                        # required.
                        if self._is_reference(m):
                            check = ("(({0} && rhs.{0} && *{0} == *rhs.{0}) ||"
                                     "(!{0} && !rhs.{0}))").format(m.name)
                        else:
                            check = "({0} == rhs.{0})".format(m.name)

                        ctype = self._get_true_type(m.type)
                        if ctype.is_base_type and ctype.as_base_type.is_binary:
                            check = "apache::thrift::StringTraits<{0}>::" \
                                "isEqual({1}, rhs.{1})".format(
                                self._type_name(ctype), m.name)
                        if m.req != e_req.optional or not self._has_isset(m):
                            with out('if (!({0}))'.format(
                                    check)):
                                out('return false;')
                        else:
                            with out('if (__isset.{0} != rhs.__isset.{0})'
                                    .format(m.name)):
                                out('return false;')
                            with out('else if'
                                    ' (__isset.{0} && !({1}))'
                                    .format(m.name, check)):
                                out('return false;')
                    out('return true;')

            # Generate the declaration of a less-than operator. This must be
            # implemented by the application developer if they wish to use it.
            # (They will get a link error if they try to use it without an
            # implementation.)i
            if self._is_orderable_type(obj) and \
              'no_default_comparators' not in obj.annotations:
                with struct.defn('bool operator < (const {0}& rhs) const'
                  .format(obj.name), in_header=True):
                    if obj.is_union:
                        out('if (type_ != rhs.type_)'
                            ' { return type_ < rhs.type_; }')
                        self._gen_union_switch(members,
                            'return value_.{field} < rhs.value_.{field};',
                            'return *value_.{field} < *rhs.value_.{field};',
                            default='return false;', nobreak=True)
                    else:
                        for m in members:
                            with out('if (!({0} == rhs.{0}))'
                                    .format(m.name)):
                                out('return {0} < rhs.{0};'.format(m.name))
                        else:
                            out('(void)rhs;')
                        out('return false;')
            else:
                struct('bool operator < (const {0}& rhs) const;'
                  .format(obj.name))

        # generate getters/setters for structures not using folly::Optional
        if not (obj.is_union or self.flag_optionals or
                self.flag_no_getters_setters):
            for member in members:
                # Doesn't make sense to generate getters/setters for ref fields.
                # (There is no __isset bit for refs.)
                if self._is_reference(member):
                    continue
                t = self._type_name(member.type)
                getter_name = 'get_' + member.name
                if getter_name in [m.name for m in members]:
                    raise CompilerError("Cannot have object with fields with "
                        "names of form 'field' and 'get_field'. (In structure "
                        "{0}: fields '{1}' and '{2}'.)"
                        .format(obj.name, member.name, getter_name))
                ttype = self._get_true_type(member.type)
                outofline = not ttype.is_base_type and \
                            not ttype.is_enum and \
                            not member.type in self._generated_types
                if member.req == e_req.optional:
                    # getter overload 1: const T* get_field() const&
                    with struct.defn('const {0}* {{name}}() const&'
                                     .format(t),
                                     name=getter_name,
                                     in_header=not outofline):
                        out('return __isset.{0} ? std::addressof({0})'
                            ' : nullptr;'
                            .format(member.name))
                    # getter overload 2: T* get_field() &
                    with struct.defn('{0}* {{name}}() &'.format(t),
                                     name=getter_name,
                                     in_header=not outofline):
                        out('return __isset.{0} ? std::addressof({0})'
                            ' : nullptr;'
                            .format(member.name))
                    # getter overload 3: T* get_field() && = delete
                    out('{0}* {1}() && = delete;'.format(t, getter_name))
                else:
                    if not self._is_complex_type(member.type):
                        # getter: T get_field() const
                        with struct.defn('{0} {{name}}() const'
                                         .format(t),
                                         name=getter_name,
                                         in_header=True):
                            out('return {0};'.format(member.name))
                    else:
                        # getter overload 1: const T& get_field() const&
                        with struct.defn('const {0}& {{name}}() const&'
                                         .format(t),
                                         name=getter_name,
                                         in_header=not outofline):
                            out('return {0};'.format(member.name))
                        # getter overload 2: T get_field() &&
                        with struct.defn('{0} {{name}}() &&'.format(t),
                                         name=getter_name,
                                         in_header=not outofline):
                            out('return std::move({0});'.format(member.name))

                # setters
                setter_name = 'set_' + member.name
                if setter_name in [m.name for m in members]:
                    raise CompilerError("Cannot have object with fields with "
                        "names of form 'field' and 'set_field'. (In structure "
                        "{0}: fields '{1}' and '{2}'.)"
                        .format(obj.name, member.name, setter_name))
                param_name = member.name + '_'
                if not self._is_complex_type(member.type):
                    # setter: T& set_field(T t)
                    with struct.defn('{0}& {{name}}({0} {1})'
                                     .format(t, param_name),
                                     name=setter_name,
                                     in_header=True):
                        out('{0} = {1};'.format(member.name, param_name))
                        if self._has_isset(member):
                            out('__isset.{0} = true;'.format(member.name))
                        out('return {0};'.format(member.name))
                else:
                    # rely on compiler to generate appropriate pass-by-ref
                    # setters
                    setter_arg = 'T_{0}_{1}_struct_setter'.format(
                        obj.name, member.name)
                    with struct.defn(('template <typename {0}>\n'
                                    '{1}& {{name}}({0}&& {2})')
                                    .format(setter_arg, t, param_name),
                                    name=setter_name,
                                    in_header=True):
                        out('{0} = std::forward<{1}>({2});'
                            .format(member.name, setter_arg, param_name))
                        if self._has_isset(member):
                            out('__isset.{0} = true;'.format(member.name))
                        out('return {0};'.format(member.name))

        # generate union accessors/settors
        if obj.is_union:
            for member in members:
                mtype = member.type
                t = self._type_name(mtype)
                true_type = self._get_true_type(mtype)
                is_reference = self._is_reference(member)
                field_type_name = self._field_type_name(member, true_type)
                setter_result = field_type_name
                if not is_reference:
                    field_type_name = t
                    setter_result = t
                setter_result += '&'
                # generate definition on the tcc file if layout information
                # is incomplete (say, recursive types)
                outofline = is_reference and not mtype.is_base_type \
                    and not mtype in self._generated_types
                is_primitive = (mtype.is_base_type and not mtype.is_string) \
                               or mtype.is_enum
                if is_primitive:
                    with struct.defn('{{result_type}} {{symbol_scope}}'
                                     'set_{{symbol_name}}({0} t = {0}())'
                                     .format(t),
                                   name=member.name,
                                   symbol_name=member.name,
                                   result_type=setter_result,
                                   in_header=True):
                        out('__clear();')
                        out('type_ = Type::{0};'.format(member.name))
                        if is_reference:
                            out(('::new (std::addressof(value_.{0})) '
                                '{1}(new {1}::element_type(t));')
                                .format(member.name, field_type_name))
                        else:
                            out('::new (std::addressof(value_.{0})) {1}(t);'
                                .format(member.name, field_type_name))
                        out('return value_.{0};'.format(member.name))
                else:
                    with struct.defn('{{result_type}} {{symbol_scope}}'
                                     'set_{{symbol_name}}({0} const &t)'
                                     .format(t),
                                   name=member.name,
                                   symbol_name=member.name,
                                   result_type=setter_result,
                                   in_header=not outofline):
                        out('__clear();')
                        out('type_ = Type::{0};'.format(member.name))
                        if is_reference:
                            out(('::new (std::addressof(value_.{0})) '
                                '{1}(new {1}::element_type(t));')
                                .format(member.name, field_type_name))
                        else:
                            out('::new (std::addressof(value_.{0})) {1}(t);'
                                .format(member.name, field_type_name))
                        out('return value_.{0};'.format(member.name))
                    with struct.defn('{{result_type}} {{symbol_scope}}'
                                     'set_{{symbol_name}}({0}&& t)'.format(t),
                                   name=member.name,
                                   symbol_name=member.name,
                                   result_type=setter_result,
                                   in_header=not outofline):
                        out('__clear();')
                        out('type_ = Type::{0};'.format(member.name))
                        if is_reference:
                            out(('::new (std::addressof(value_.{0})) '
                                '{1}(new {1}::element_type(std::move(t)));')
                                .format(member.name, field_type_name))
                        else:
                            out(('::new (std::addressof(value_.{0})) '
                                '{1}(std::move(t));')
                                .format(member.name, field_type_name))
                        out('return value_.{0};'.format(member.name))
                    with struct.defn('template<typename... T, typename '
                                  '{tpl_default_0}> {result_type} '
                                  '{symbol_scope}set_{symbol_name}(T&&... t)',
                                   name=member.name,
                                   symbol_name=member.name,
                                   result_type=setter_result,
                                   tpl_default_0='::apache::thrift::'
                                    'safe_overload_t<{0}, T...>'.format(t),
                                   output=self._out_tcc if outofline else None,
                                   in_header=not outofline):
                        out('__clear();')
                        out('type_ = Type::{0};'.format(member.name))
                        if is_reference:
                            out(('::new (std::addressof(value_.{0})) '
                                '{1}(new {1}::element_type('
                                'std::forward<T>(t)...));')
                                .format(member.name, field_type_name))
                        else:
                            out(('::new (std::addressof(value_.{0})) '
                                '{1}(std::forward<T>(t)...);')
                                .format(member.name, field_type_name))
                        out('return value_.{0};'.format(member.name))

            for member in members:
                t = self._type_name(member.type)
                if self._is_reference(member):
                    true_type = self._get_true_type(member.type)
                    t = self._field_type_name(member, true_type)
                with struct.defn('{result_type} {symbol_scope}'
                               + 'get_{symbol_name}() const',
                               name=member.name,
                               symbol_name=member.name,
                               result_type=t + ' const &',
                               in_header=True):
                    out('assert(type_ == Type::{0});'.format(member.name))
                    out('return value_.{0};'.format(member.name))

            for member in members:
                t = self._type_name(member.type)
                if self._is_reference(member):
                    true_type = self._get_true_type(member.type)
                    t = self._field_type_name(member, true_type)
                with struct.defn('{result_type} {symbol_scope}'
                               + 'mutable_{symbol_name}()',
                               name=member.name,
                               symbol_name=member.name,
                               result_type=t + ' &',
                               in_header=True):
                    out('assert(type_ == Type::{0});'.format(member.name))
                    out('return value_.{0};'.format(member.name))

            for member in members:
                t = self._type_name(member.type)
                if self._is_reference(member):
                    true_type = self._get_true_type(member.type)
                    t = self._field_type_name(member, true_type)
                with struct.defn('{result_type} {symbol_scope}'
                               + 'move_{symbol_name}()',
                               name=member.name,
                               symbol_name=member.name,
                               result_type=t,
                               in_header=True):
                    out('assert(type_ == Type::{0});'.format(member.name))
                    out('return std::move(value_.{0});'.format(member.name))

            struct()
            struct('Type getType() const { return type_; }')

        if read or write:
            struct()
        if read:
            self._generate_struct_reader(struct, obj, pointers)
        if write:
            for zc in False, True:
                self._generate_struct_compute_length(
                        struct, obj, pointers, result, zero_copy=zc)
            self._generate_struct_writer(struct, obj, pointers, result)
        if is_exception:
            if 'message' in obj.annotations:
                what = '{0}.c_str()'.format(obj.annotations['message'])
            else:
                what = '"{0}"'.format(self._type_name(obj))
            with struct.defn('const char* what() const noexcept override',
                                   in_header=True) as x1:
                x1('return {0};'.format(what))

        # generate the union protected members
        if obj.is_union:
            struct.label('protected:')
            with struct.defn('template <class T>\n' + 'void destruct(T &val)',
                            in_header=True):
                out('(&val)->~T();')
            struct()
            struct('Type type_;')
            struct('storage_type value_;')

        if self._has_cpp_annotation(obj, 'methods'):
            struct('// user defined code (cpp2.methods = ...)')
            struct(self._cpp_annotation(obj, 'methods'))

        struct()
        struct.label('private:')
        self._generate_struct_reader_translate_field_name(struct, obj)
        # we're done with the struct definition
        struct.release()

        if swap:
            s = self._types_scope
            # Generate a namespace-scope swap() function
            with s.defn('void {{name}}({0}& a, {0}& b)'.format(obj.name),
                        name='swap'):
                if obj.is_union:
                    # For unions, the members cannot be swapped individually
                    # so instead we use the logic in the move constructors to
                    # swap the object wholesale
                    out('{0} temp(std::move(a));'.format(obj.name))
                    out('a = std::move(b);')
                    out('b = std::move(temp);')
                else:
                    # Let argument-dependent name lookup find the correct swap()
                    # function to use based on the argument types. If none is found
                    # in the arguments' namespaces, fall back to ::std::swap().
                    out('using ::std::swap;')
                    has_nonrequired_fields = False
                    for tfield in members:
                        ttype = self._get_true_type(tfield.type)
                        if tfield.req != e_req.required:
                            has_nonrequired_fields = True
                        out('swap(a.{0}, b.{0});'.format(tfield.name))
                    if should_generate_isset:
                        out('swap(a.__isset, b.__isset);')
                    if struct_options.has_serialized_fields:
                        out('swap(a.{0}, b.{0});'.format(
                                self._serialized_fields_protocol_name))
                        out('swap(a.{0}, b.{0});'.format(
                                self._serialized_fields_name))
                    if not (
                            members or should_generate_isset or
                            struct_options.has_serialized_fields):
                        out('(void)a;')
                        out('(void)b;')
        write_extern_templates()

    # ======================================================================
    # DESERIALIZATION CODE
    # ======================================================================

    def _generate_struct_reader_translate_field_name(self, scope, obj):
        with scope.defn(
            'void {name}('
            'FOLLY_MAYBE_UNUSED folly::StringPiece _fname, '
            'FOLLY_MAYBE_UNUSED int16_t& fid, '
            'FOLLY_MAYBE_UNUSED apache::thrift::protocol::TType& _ftype)',
            name='translateFieldName', modifiers='static') as s:
            with s('if (false)'):
                pass
            for field in obj.members:
                with s('else if (_fname == "{0}")'.format(field.name)):
                    s('fid = {0};'.format(field.key))
                    s('_ftype = {0};'.format(self._type_to_enum(field.type)))

    def _generate_struct_reader(self, scope, obj,
                                pointers=False, has_isset=True,
                                is_service_helper=False):
        if self.flag_optionals:
            has_isset = False
        this = 'this'
        d = scope.defn('template <class Protocol_>\n'
                       'uint32_t {name}(Protocol_* iprot)', name='read',
                       output=self._out_tcc)
        if obj.is_union:
            has_isset = False

        if self.flag_lean_mean_meta_machine and not is_service_helper:
            s = d.scope
            with s:
                s('return ::apache::thrift::serializer_read(*{}, *iprot);'.
                    format(this))
            return

        fields = obj.members

        struct_options = self._get_serialized_fields_options(obj)
        # s = scope of the read() function
        s = d.scope
        # Declare stack tmp variables
        s('auto _xferStart = iprot->getCurrentPosition().getCurrentPosition();')
        s('std::string _fname;')
        s('apache::thrift::protocol::TType _ftype;')
        s('int16_t fid;')
        s()
        s('iprot->readStructBegin(_fname);')
        s()
        s('using apache::thrift::TProtocolException;')
        s()
        # Special handling for serialized fields
        if struct_options.has_serialized_fields:
            s('{0}->{1} = iprot->protocolType();'.format(
                this, self._serialized_fields_protocol_name))
            s('std::unique_ptr<folly::IOBuf> serialized;')
        # Required variables aren't in __isset, so we need tmp vars to
        # check them.
        req_fields = ifilter(lambda field: field.req == e_req.required,
                             fields)
        for field in req_fields:
            s('bool isset_{0.name} = false;'.format(field))
        s()

        fields_scope = None
        if obj.is_union:
            # Unions only have one member set, so don't loop
            s('iprot->readFieldBegin(_fname, _ftype, fid);')
            s1 = s('if (_ftype == apache::thrift::protocol::T_STOP)').scope
            s1(this + '->__clear();')
            s1.release()
            fields_scope = s1 = s.sameLine('else').scope
        else:
            # Loop over reading in fields
            s1 = s('while (true)').scope
            # Save the position before the field beginning
            if struct_options.has_serialized_fields:
                s1('auto fbegin = iprot->getCurrentPosition();')
                s1('bool fserialized = false;')
            # Read beginning field marker
            s1('iprot->readFieldBegin(_fname, _ftype, fid);')
            # Check for field STOP marker
            with s1('if (_ftype == apache::thrift::protocol::T_STOP)'):
                out('break;')
            fields_scope = s

        with s1('if (iprot->kUsesFieldNames())'):
            s1(this + '->translateFieldName(_fname, fid, _ftype);')

        # Switch statement on the field we are reading
        s2 = fields_scope('switch (fid)').scope
        # Generate deserialization code for known cases
        for field in fields:
            s3 = s2.case(field.key).scope
            if ('format' in field.annotations and
                    field.annotations['format'] == 'serialized'):
                s3('fserialized = true;')
                s3('iprot->skip(_ftype);')
                s3.release()  # "break;"
                continue
            s4 = s3('if (_ftype == {0})'.format(self._type_to_enum(
                    field.type))).scope
            with s4:
                field_prefix = this + '->'
                field_suffix = ''
                if obj.is_union:
                    s4(field_prefix + 'set_{0}();'.format(field.name))
                    field_prefix += 'mutable_'
                    field_suffix = '()'
                if pointers and not field.type.is_xception:
                    # This is only used for read pargs, so a const-cast is okay
                    # since the struct is exposed in generated code only.
                    self._generate_deserialize_field(
                        s4, field,
                        '(*const_cast<{0}*>('.format(
                                self._type_name(field.type)) +
                                                field_prefix,
                                                field_suffix + '))')
                else:
                    self._generate_deserialize_field(s4, field, field_prefix,
                                                     field_suffix)
                if has_isset and self._has_isset(field):
                    s4('{0}->__isset.{1} = true;'.format(this, field.name))
                elif field.req == e_req.required:
                    s4('isset_{1} = true;'.format(this, field.name))
            with s3.sameLine('else'):
                out('iprot->skip(_ftype);')
                # TODO(dreiss): Make this an option when thrift structs have a
                # common base class.
                # s4('throw TProtocolException(TProtocolException::'
                #    'INVALID_DATA);')
            s3.release()  # "break;"
        # Default case
        with s2.case('default'):
            out('iprot->skip(_ftype);')
            if struct_options.keep_unknown_fields:
                out('fserialized = true;')
        s2.release()  # switch
        # Read field end marker
        s1('iprot->readFieldEnd();')
        # Eat the stop byte that terminates union content
        if obj.is_union:
            s1('iprot->readFieldBegin(_fname, _ftype, fid);')
            with s1('if (UNLIKELY(_ftype != {proto_ns}::T_STOP))'
                    .format(proto_ns="apache::thrift::protocol")).scope:
                s1('using apache::thrift::protocol::TProtocolException;')
                s1('TProtocolException::throwUnionMissingStop();')
        if struct_options.has_serialized_fields:
            with s1('if (fserialized)').scope:
                out('iprot->readFromPositionAndAppend(fbegin, serialized);')
        s1.release()  # while(true)
        s('iprot->readStructEnd();')
        # Finalize serialized fields buffer if necessary
        if struct_options.has_serialized_fields:
            s()
            # Thrift is supposed to be called only with IOBuf that manage
            # underlying buffer. Thus it's safe to store IOBuf pointing to the
            # parts of original buffer inside the deserialized struct.
            # Note: it might be somewhat memory inefficient as we might be
            # pointing to a small chunk of the big buffer while keeping the
            # entire buffer around. However, the current implementation leaves
            # it to the application to call coalesce() on __serialized field
            # if necessary.
            with s('if (serialized)'):
                out(('{0}->{1} = std::move(serialized);').format(
                       this, self._serialized_fields_name))
        # Throw if any required fields are missing.
        # We do this after reading the struct end so that there might possibly
        # be a chance of continuing.
        s()
        for field in filter(self._should_generate_field, fields):
            if not field.req == e_req.required:
                continue
            with s('if (!isset_{0.name})'.format(field)):
                out('TProtocolException::throwMissingRequiredField("{0}", "{1}");'
                    .format(field.name, obj.name))
        s('return iprot->getCurrentPosition().getCurrentPosition() - _xferStart;')
        s.release()  # the function

    def _generate_deserialize_field(self, scope, field, prefix='', suffix=''):
        'Deserializes a field of any type.'
        name = prefix + field.name + self._type_access_suffix(field.type) + \
                suffix
        self._generate_deserialize_type(
            scope, field.type, name, self._is_reference(field),
            self._is_optional_wrapped(field))

    def _generate_deserialize_type(self, scope, otype, name,
                                   pointer=False, optional_wrapped=False):
        'Deserializes a variable of any type.'
        ttype = self._get_true_type(otype)
        s = scope
        if ttype.is_void:
            raise TypeError('CANNOT GENERATE DESERIALIZE CODE FOR void TYPE: '\
                            + name)

        if ttype.is_struct or ttype.is_xception:
            self._generate_deserialize_struct(
                scope, otype, ttype.as_struct, name, pointer, optional_wrapped)
        elif ttype.is_container or ttype.is_enum:
            self._generate_templated_deserialize_container(
                scope, otype,
                ttype.as_container,
                name, ttype, pointer,
                optional_wrapped)
        elif ttype.is_base_type:
            if optional_wrapped:
                # assign a new default-constructed value into the Optional
                s('{0} = {1}();'.format(name, self._type_name(ttype)))
            btype = ttype.as_base_type
            base = btype.base
            if base == t_base.void:
                raise CompilerError("Cannot deserialize void field in a "
                    "struct: " + name)
            elif base == t_base.string:
                if btype.is_binary:
                    txt = 'readBinary({0})'
                else:
                    txt = 'readString({0})'
            elif base == t_base.bool:
                txt = 'readBool({0})'
            elif base == t_base.byte:
                txt = 'readByte({0})'
            elif base == t_base.i16:
                txt = 'readI16({0})'
            elif base == t_base.i32:
                txt = 'readI32({0})'
            elif base == t_base.i64:
                txt = 'readI64({0})'
            elif base == t_base.double:
                txt = 'readDouble({0})'
            elif base == t_base.float:
                txt = 'readFloat({0})'
            else:
                raise CompilerError('No C++ reader for base type ' + \
                        btype.t_base_name(base) + name)
            dest = name
            if pointer:
                dest = '(*{0})'.format(name)
            if optional_wrapped:
                dest += ".value()"
            txt = 'iprot->{0};'.format(txt.format(dest))
            s(txt)
        else:
            raise TypeError(("DO NOT KNOW HOW TO DESERIALIZE '{0}' "
                             "TYPE {1}").format(name, self._type_name(ttype)))

    def _generate_deserialize_struct(self, scope, otype, struct, prefix,
                                     pointer=False, optional_wrapped=False):
        s = scope
        if pointer:
            s('std::unique_ptr<{0}> ptr = std::make_unique<{0}>();'.format(
                self._type_name(otype)))
            s('::apache::thrift::Cpp2Ops< {0}>::read('
                'iprot, ptr.get());'.format(self._type_name(otype)))
            s('{0} = std::move(ptr);'.format(prefix))
        elif optional_wrapped:
            scope("{0} = {1}();".format(
                prefix, self._type_name(otype)))
            scope('::apache::thrift::Cpp2Ops< {0}>::read('
                  'iprot, &{1}.value());'.format(
                      self._type_name(otype), prefix))
        else:
            scope('::apache::thrift::Cpp2Ops< {0}>::read('
                  'iprot, &{1});'.format(
                      self._type_name(otype), prefix))

    def _render_indirection_struct_name(self, program_name, typedef_name):
        return 'apache_thrift_indirection_{0}_{1}'.format(
            program_name, re.sub('[\W]+', '_', typedef_name))

    def _print_indirection_set(self, indirection_set):
        for program_name, typedef_name, indirection in indirection_set:
            indirection_struct_name = self._render_indirection_struct_name(
                program_name, typedef_name)
            with self._types_scope.cls('struct {0}'.format(indirection_struct_name)) as s:
                with s.defn(
                        'template <typename T> static auto&& {name}(T&& x)',
                        name='get',
                        in_header=True):
                    out('return std::forward<T>(x){0};'.format(indirection))
                with s.defn(
                        'template <typename T> static auto&& {name}(T const&& x)',
                        name='get',
                        in_header=True):
                    out('return std::forward<T>(x){0};'.format(indirection))

    def _render_indirection_struct(self, ttype):
        typedef_name = ttype
        while ttype.is_typedef:
            ttype = ttype.type

        if self._has_cpp_annotation(ttype, 'indirection'):
            return set([(
                self._program.name,
                typedef_name.symbolic,
                self._cpp_annotation(ttype, 'indirection'))])
        if ttype.is_map:
            return set(
                self._render_indirection_struct(ttype.as_map.key_type) |
                self._render_indirection_struct(ttype.as_map.value_type))
        elif ttype.is_set:
            return self._render_indirection_struct(ttype.as_set.elem_type)
        elif ttype.is_list:
            return self._render_indirection_struct(ttype.as_list.elem_type)

        return set()

    def _generate_indirection(self, program):
        indirections = set()
        for obj in program.objects:
            for field in obj.members:
                indirections |= self._render_indirection_struct(field.type)
        self._print_indirection_set(indirections)

    def _render_type_class_for_serialization(self, ttype, annotation=False):
        typedef_name = ttype
        while ttype.is_typedef:
            ttype = ttype.type

        if ttype.is_void:
            return '::apache::thrift::type_class::nothing'
        elif self._has_cpp_annotation(ttype, 'indirection') and not annotation:
            return (
                    '::apache::thrift::detail::pm::IndirectionTag<{0}, {1}>'.format(
                    self._render_indirection_struct_name(
                        self._program.name,
                        typedef_name.symbolic),
                    self._render_type_class_for_serialization(ttype, True)))
        elif ttype.is_base_type and ttype.as_base_type.is_binary:
            return '::apache::thrift::type_class::binary'
        elif ttype.is_string:
            return '::apache::thrift::type_class::string'
        elif ttype.is_floating_point:
            return '::apache::thrift::type_class::floating_point'
        elif ttype.is_base_type:
            return '::apache::thrift::type_class::integral'
        elif ttype.is_enum:
            return '::apache::thrift::type_class::enumeration'
        elif ttype.is_list:
            return '::apache::thrift::type_class::list<{0}>'.format(
                self._render_type_class_for_serialization(ttype.as_list.elem_type))
        elif ttype.is_map:
            if 'forward_compatibility' not in ttype.annotations:
                return '::apache::thrift::type_class::map<{0}, {1}>'.format(
                    self._render_type_class_for_serialization(ttype.as_map.key_type),
                    self._render_type_class_for_serialization(ttype.as_map.value_type))
            else:
                return '::apache::thrift::type_class::map_forward_compatibility<{0}, {1}>'.format(
                    self._render_type_class_for_serialization(ttype.as_map.key_type),
                    self._render_type_class_for_serialization(ttype.as_map.value_type))
        elif ttype.is_set:
            return '::apache::thrift::type_class::set<{0}>'.format(
                self._render_type_class_for_serialization(ttype.as_set.elem_type))
        elif ttype.is_xception:
            return '::apache::thrift::type_class::structure'
        elif ttype.is_struct:
            return '::apache::thrift::type_class::structure'

        return '::apache::thrift::type_class::unknown'

    def _generate_templated_deserialize_container(self, scope, otype, struct,
                                                  prefix, ttype, pointer=False,
                                                  optional_wrapped=False):
        s = scope
        if pointer:
            s('std::unique_ptr<{0}> ptr = std::make_unique<{0}>();'.format(
                self._type_name(otype)))
            s('::apache::thrift::detail::pm::protocol_methods'
                    '< {0}, {1}>::read(*iprot, *ptr);'.format(
                        self._render_type_class_for_serialization(otype),
                        self._type_name(otype)))
            s('{0} = std::move(ptr);'.format(prefix))
        elif optional_wrapped:
            s("{0} = {1}();".format(prefix, self._type_name(otype)))
            s('::apache::thrift::detail::pm::protocol_methods'
                    '< {0}, {1}>::read(*iprot, {2}.value());'.format(
                        self._render_type_class_for_serialization(otype),
                        self._type_name(otype),
                        prefix))
        else:
            if not ttype.is_enum:
                s("{0} = {1}();".format(prefix, self._type_name(otype)))
            s('::apache::thrift::detail::pm::protocol_methods'
                    '< {0}, {1}>::read(*iprot, {2});'.format(
                        self._render_type_class_for_serialization(otype),
                        self._type_name(otype),
                        prefix))

    # ======================================================================
    # SERIALIZATION CODE
    # ======================================================================

    def _generate_struct_compute_length(self, scope, obj,
                                        pointers=False,
                                        result=False,
                                        zero_copy=False,
                                        is_service_helper=False):
        method = "serializedSizeZC" if zero_copy else "serializedSize"
        this = 'this'
        d = scope.defn('template <class Protocol_>\n'
                    'uint32_t {name}(Protocol_ const* prot_) const',
                    name=method,
                    output=self._out_tcc)

        struct_options = self._get_serialized_fields_options(obj)

        s = d.scope

        if self.flag_lean_mean_meta_machine and not is_service_helper:
            with s:
                if zero_copy:
                    s('return ::apache::thrift::'
                      'serializer_serialized_size_zc(*{}, *prot_);'.format(
                        this))
                else:
                    s('return ::apache::thrift::'
                      'serializer_serialized_size(*{}, *prot_);'.format(
                        this))
            return

        if struct_options.has_serialized_fields:
            with s('if ({0}->{1} && '
                    'prot_->protocolType() != {0}->{2})'.format(
                        this,
                        self._serialized_fields_name,
                        self._serialized_fields_protocol_name)):
                out('using apache::thrift::TProtocolException;')
                out('throw TProtocolException(TProtocolException::BAD_VERSION);')
        s('uint32_t xfer = 0;')
        s('xfer += prot_->serializedStructSize("{0}");'.format(obj.name))

        # unions need a case statement to select which member is active
        s0 = s
        if obj.is_union:
            s = s0('switch({0}->getType())'.format(this)).scope

        first = True
        for field in filter(self._should_generate_field, obj.members):
            if self._is_reference(field):
                isset_expr_format = '{0}->{1}'
            elif self._is_optional_wrapped(field):
                isset_expr_format = '{0}->{1}.hasValue()'
            else:
                isset_expr_format = '{0}->__isset.{1}'

            isset_expr = isset_expr_format.format(this, field.name)

            if result == True:
                if first:
                    first = False
                    s1 = s('if ({0})'.format(isset_expr)).scope
                else:
                    s1 = s('else if ({0})'.format(isset_expr)).scope
            elif field.req == e_req.optional:
                s1 = s('if ({0})'.format(isset_expr)).scope
            elif obj.is_union:
                s1 = s.case(obj.name + '::Type::' + field.name).scope
            elif self.flag_terse_writes and not field.req == e_req.required:
                s1 = self._try_terse_write(field, this, s, pointers)
            else:
                s1 = s

            # Add the size of field header + footer
            s1('xfer += prot_->serializedFieldSize("{0}", {1}, {2});'
               ''.format(field.name, self._type_to_enum(field.type),
                                field.key))
            # Add the sizes of field contents
            field_prefix = this + '->'
            field_suffix = ''
            if obj.is_union:
                field_prefix += 'get_'
                field_suffix = '()'
            if pointers and not field.type.is_xception:
                self._generate_serialize_field(
                    s1, field,
                    '(*const_cast<{0}*>('.format(
                            self._type_name(field.type)) + field_prefix,
                    field_suffix + '))',
                    method="serializedSize",
                    struct_method=method,
                    binary_method=method)
            else:
                self._generate_serialize_field(s1, field, field_prefix,
                                               field_suffix,
                                               method="serializedSize",
                                               struct_method=method,
                                               binary_method=method)
            if s1 is not s:
                s1.release()  # if this->__isset.{0}
        if s0 is not s:
            s('case ' + obj.name + '::Type::__EMPTY__:;')
            s.release()
            s = s0

        if struct_options.has_serialized_fields:
            s('xfer += prot_->serializedSizeSerializedData({0}->{1});'
                    .format(this, self._serialized_fields_name))
        s('xfer += prot_->serializedSizeStop();')
        s('return xfer;')
        s.release()

    def _try_terse_write(self, field, this, s, pointers):
        'Generates a terse write predicate for unspecified field, if possible'
        t = self._get_true_type(field.type)
        # Not possible for void/struct/exception.
        if t.is_void or t.is_struct or t.is_xception:
            return s

        # Terse write is unsafe to use without explicitly setting default
        # value as in PHP / Python that would change result of deserialization
        # (comparing with the case when terse_writes is not used): field set
        # in C++ to default value would be deserialized as null / None.
        if self.safe_terse_writes and field.value is None:
            return s

        cmpval = (('(*' + this + '->{0})') if pointers else
                  ('' + this + '->{0}')).format(field.name)

        # For strings, containers - only support predicate if default
        # value is empty.
        if t.is_string:
            if not field.value or len(field.value.string) == 0:
                return s('if (!apache::thrift::StringTraits< {0}>::'
                         'isEmpty({1}))'.format(
                             self._type_name(t), cmpval)).scope
            else:
                return s

        if t.is_container:
            if not field.value:  # only support empty default.
                return s('if (!{0}.empty())'.format(cmpval)).scope
            else:
                return s  # Otherwise, no terse_write possible

        # For base type/enum, check vs. default const value.
        if t.is_base_type or t.is_enum:
            return s('if ({0} != {1})'.format(
                    cmpval, self._member_default_value(field,
                                                       explicit=True))).scope
        return s

    def _generate_struct_writer(self, scope, obj, pointers=False,
                                result=False,
                                is_service_helper=False):
        'Generates the write function.'
        this = 'this'
        d = scope.defn('template <class Protocol_>\nuint32_t {name}'
                       '(Protocol_* prot_) const', name='write',
                       output=self._out_tcc)

        s = d.scope
        if self.flag_lean_mean_meta_machine and not is_service_helper:
            with s:
                s("return ::apache::thrift::serializer_write(*{}, *prot_);".
                    format(this))
            return

        name = obj.name
        fields = filter(self._should_generate_field, obj.members)

        struct_options = self._get_serialized_fields_options(obj)

        if struct_options.has_serialized_fields:
            with s('if ({0}->{1} && '
                    'Protocol_::protocolType() != {0}->{2})'.format(
                        this,
                        self._serialized_fields_name,
                        self._serialized_fields_protocol_name)).scope:
                out('using apache::thrift::TProtocolException;')
                out('throw TProtocolException(TProtocolException::BAD_VERSION);')

        s('uint32_t xfer = 0;')
        s('xfer += prot_->writeStructBegin("{0}");'.format(name))

        # unions need a case statement to select which member is active
        s0 = s
        if obj.is_union:
            s = s0('switch({0}->getType())'.format(this)).scope

        first = True
        for field in fields:
            if self._is_reference(field):
                isset_expr_format = '{0}->{1}'
            elif self._is_optional_wrapped(field):
                isset_expr_format = '{0}->{1}.hasValue()'
            else:
                isset_expr_format = '{0}->__isset.{1}'

            isset_expr = isset_expr_format.format(this, field.name)

            if result == True:
                if first:
                    first = False
                    s1 = s('if ({0})'.format(isset_expr)).scope
                else:
                    s1 = s('else if ({0})'.format(isset_expr)).scope
            elif field.req == e_req.optional:
                s1 = s('if ({0})'.format(isset_expr)).scope
            elif obj.is_union:
                s1 = s.case(obj.name + '::Type::' + field.name).scope
            elif self.flag_terse_writes and not field.req == e_req.required:
                s1 = self._try_terse_write(field, this, s, pointers)
            else:
                s1 = s
            # Write field header
            s1('xfer += prot_->writeFieldBegin("{0}", {1}, {2});'.format(
                field.name, self._type_to_enum(field.type), field.key))
            # Write field contents
            field_prefix = this + '->'
            field_suffix = ''
            if obj.is_union:
                field_prefix += 'get_'
                field_suffix = '()'
            if pointers and not field.type.is_xception:
                self._generate_serialize_field(
                    s1, field,
                    '(*const_cast<{0}*>('.format(
                        self._type_name(field.type)) + field_prefix,
                    field_suffix + '))')
            else:
                self._generate_serialize_field(s1, field, field_prefix,
                                               field_suffix)
            # Write field closer
            s1('xfer += prot_->writeFieldEnd();')
            if s1 is not s:
                s1.release()  # if this->_isset.{0}
        if s0 is not s:
            s('case ' + obj.name + '::Type::__EMPTY__:;')
            s.release()
            s = s0
        # Flush any fields stored in serialized form
        if struct_options.has_serialized_fields:
            s('xfer += prot_->writeSerializedData({0}->{1});'.format(
                this, self._serialized_fields_name))
        # Write the struct map
        s('xfer += prot_->writeFieldStop();')
        s('xfer += prot_->writeStructEnd();')
        s('return xfer;')
        s.release()  # the function

    def _generate_serialize_field(self, scope, tfield, prefix='', suffix='',
                                  method='write',
                                  struct_method=None,
                                  binary_method=None):
        'Serializes a field of any type.'
        name = prefix + tfield.name + self._type_access_suffix(tfield.type) + \
                suffix
        pointer = self._is_reference(tfield)
        self._generate_serialize_type(scope, tfield.type, name,
                                      self._is_optional_wrapped(tfield),
                                      method,
                                      struct_method, binary_method, pointer)

    def _generate_serialize_type(self, scope, otype, name,
                                 is_optional_wrapped,
                                 method='write',
                                 struct_method=None,
                                 binary_method=None,
                                 pointer=False):
        'Serializes a variable of any type.'
        ttype = self._get_true_type(otype)
        if struct_method is None:
            struct_method = method
        if binary_method is None:
            binary_method = method

        val_expr = name
        if is_optional_wrapped:
            val_expr += ".value()"

        # Do nothing for void types
        if ttype.is_void:
            raise TypeError('CANNOT GENERATE SERIALIZE CODE FOR void TYPE: '\
                            + name)
        if ttype.is_struct or ttype.is_xception:
            self._generate_serialize_struct(scope, otype, ttype.as_struct,
                                            val_expr,
                                            struct_method, pointer)
        elif ttype.is_container or ttype.is_enum:
            self._generate_serialize_container(scope, ttype.as_container, otype,
                                               pointer,
                                               val_expr,
                                               method,
                                               struct_method=struct_method,
                                               binary_method=binary_method)
        elif ttype.is_base_type:
            btype = ttype.as_base_type
            base = btype.base
            if base == t_base.void:
                raise CompilerError('Cannot serialize void field in a '
                                    'struct: ' + name)
            elif base == t_base.string:
                if btype.is_binary:
                    txt = '{binary_method}Binary({name});'
                else:
                    txt = '{method}String({val_expr});'
            elif base == t_base.bool:
                txt = '{method}Bool({val_expr});'
            elif base == t_base.byte:
                txt = '{method}Byte({val_expr});'
            elif base == t_base.i16:
                txt = '{method}I16({val_expr});'
            elif base == t_base.i32:
                txt = '{method}I32({val_expr});'
            elif base == t_base.i64:
                txt = '{method}I64({val_expr});'
            elif base == t_base.double:
                txt = '{method}Double({val_expr});'
            elif base == t_base.float:
                txt = '{method}Float({val_expr});'
            else:
                raise CompilerError('No C++ writer for base type ' + \
                        btype.t_base_name(base) + name)
            if pointer:
                val_expr = '(*{0})'.format(name)
            txt = 'xfer += prot_->' + txt.format(name, **locals())
            scope(txt)
        else:
            raise TypeError(("DO NOT KNOW HOW TO SERIALIZE '{0}' "
                             "TYPE {1}").format(name, self._type_name(ttype)))

    def _generate_serialize_struct(self, scope, otype, tstruct, prefix='',
                                   method='write', pointer=False):
        if pointer:
            with scope('if ({0})'.format(prefix)):
                out('xfer += ::apache::thrift::Cpp2Ops< {0}>::{1}('
                    'prot_, {2}.get());'.format(
                        self._type_name(otype),
                        method,
                        prefix))
            with scope('else'):
                if method == "serializedSize" or method == "serializedSizeZC":
                    out('xfer += prot_->serializedStructSize(\"{0}\");'
                        .format(tstruct.name))
                    out('xfer += prot_->serializedSizeStop();')
                elif method == "write":
                    out('xfer += prot_->writeStructBegin(\"{0}\");'
                        .format(tstruct.name))
                    out('xfer += prot_->writeStructEnd();')
                    out('xfer += prot_->writeFieldStop();')
                else:
                    assert False, method
        else:
            scope('xfer += ::apache::thrift::Cpp2Ops< {0}>::{1}('
                  'prot_, &{2});'.format(
                      self._type_name(otype),
                      method,
                      prefix))

    def _generate_templated_serialize_container_internal(self, scope, otype,
                                                         pointer, prefix,
                                                         method, **kwargs):
        templated_resolution = ""
        if method == "serializedSize":
            templated_resolution = "<false>"
        elif method == "serializedSizeZC":
            templated_resolution = "<true>"

        scope('xfer += ::apache::thrift::detail::pm::protocol_methods'
                '< {0}, {1}>::{2}{3}(*prot_, {4});'.format(
                    self._render_type_class_for_serialization(otype),
                    self._type_name(otype),
                    method,
                    templated_resolution,
                    prefix))

    def _generate_serialize_container(self, scope, ttype, otype,
                                      pointer, prefix='', method='write',
                                      **kwargs):
        tte = self._type_to_enum

        if pointer:
            with scope('if ({0})'.format(prefix)):
                prefix = '*' + prefix
                self._generate_templated_serialize_container_internal(
                    scope, otype,
                    pointer, prefix,
                    method, **kwargs)
            with scope('else'):
                if ttype.is_map:
                    out('xfer += prot_->{0}MapBegin({1}, {2}, 0);'.format(
                            method,
                            tte(ttype.as_map.key_type),
                            tte(ttype.as_map.value_type)))
                    out('xfer += prot_->{0}MapEnd();'.format(method))
                elif ttype.is_set:
                    out('xfer += prot_->{0}SetBegin({1}, 0);'.format(
                            method,
                            tte(ttype.as_set.elem_type)))
                    out('xfer += prot_->{0}SetEnd();'.format(method))
                elif ttype.is_list:
                    out('xfer += prot_->{0}ListBegin({1}, 0);'.format(
                            method,
                            tte(ttype.as_list.elem_type)))
                    out('xfer += prot_->{0}ListEnd();'.format(method))
        else:
            self._generate_templated_serialize_container_internal(
                scope, otype,
                pointer, prefix,
                method, **kwargs)

    # ======================================================================
    # GENERATE STRUCT
    # ======================================================================

    def _generate_cpp2ops(self, compat, obj, scope):
        ns = self._namespace_prefix(self._get_namespace())
        compat_ns = ns
        compat_full_name = compat_ns + obj.name
        full_name = ns + obj.name

        if not compat > 0:
            with scope.defn(
                ('template <> inline '
                 'void Cpp2Ops<{compat_full_name}>::clear('
                 '{full_name}* obj)'.
                 format(**locals())), in_header=True):
                out('return obj->__clear();')

        # (method name, is void, const protocol, const object)
        ops = (('write', False, False, True),
               ('read', True, False, False),
               ('serializedSize', False, True, True),
               ('serializedSizeZC', False, True, True))

        with scope.defn('template <> inline constexpr '
                'apache::thrift::protocol::TType '
                'Cpp2Ops<{compat_full_name}>::thriftType()'
                .format(**locals()), in_header=True):
            out('return apache::thrift::protocol::T_STRUCT;')

        for method, method_is_void, prt_is_const, obj_is_const in ops:
            ret_type = 'void' if method_is_void else 'uint32_t'
            obj_const = ' const' if obj_is_const else ''
            prt_const = ' const' if prt_is_const else ''
            return_statement = '' if method_is_void else 'return '
            with scope.defn(
                ('template <> template <class Protocol> '
                 '{ret_type} Cpp2Ops<{compat_full_name}>::{method}('
                 'Protocol{prt_const}* proto, {full_name}{obj_const}* obj)'.
                 format(**locals())), name=method, in_header=True):
                out('{return_statement}obj->{method}(proto);'.
                    format(**locals()))

    def _generate_frozen_layout(self, obj, s):
        fields = sorted(obj.as_struct.members, key=lambda field: field.key)
        type_name = self._type_name(obj)

        def visitFields(fmt, fieldFmt, **kwargs):
            return fmt.format(
                type=type_name,
                fields=''.join([
                    fieldFmt.format(
                        type=self._type_name(f.type),
                        name=f.name,
                        _opt='_OPT' if f.req == e_req.optional else
                             '_REQ' if f.req == e_req.required else '',
                        id=f.key,
                        **kwargs) for f in fields]),
                **kwargs)
        s(visitFields(
            'FROZEN_TYPE({type},{fields}{view}{save}{load});',
            '\n  FROZEN_FIELD{_opt}({name}, {id}, {type})',
            view=visitFields(
                '\n  FROZEN_VIEW({fields})',
                '\n    FROZEN_VIEW_FIELD{_opt}({name}, {type})'),
            save=visitFields(
                '\n  FROZEN_SAVE_INLINE({fields})',
                '\n    FROZEN_SAVE_FIELD({name})'),
            load=visitFields(
                '\n  FROZEN_LOAD_INLINE({fields})',
                '\n    FROZEN_LOAD_FIELD({name}, {id})')))

        for (typeFmt, fieldFmt) in [
                ('FROZEN_CTOR', 'FROZEN_CTOR_FIELD{_opt}({name}, {id})'),
                ('FROZEN_MAXIMIZE', 'FROZEN_MAXIMIZE_FIELD({name})'),
                ('FROZEN_LAYOUT', 'FROZEN_LAYOUT_FIELD{_opt}({name})'),
                ('FROZEN_FREEZE', 'FROZEN_FREEZE_FIELD{_opt}({name})'),
                ('FROZEN_THAW', 'FROZEN_THAW_FIELD{_opt}({name})'),
                ('FROZEN_DEBUG', 'FROZEN_DEBUG_FIELD({name})'),
                ('FROZEN_CLEAR', 'FROZEN_CLEAR_FIELD({name})')]:
            s.impl(visitFields(typeFmt + '({type},{fields})',
                               '\n  ' + fieldFmt))


    def _should_generate_hash_equal_to(self, obj):
        annotated = obj.type if obj.is_typedef else obj
        is_enum = isinstance(obj, frontend.t_enum)
        gen_hash = is_enum or self._has_cpp_annotation(annotated, 'declare_hash')
        gen_equal_to = is_enum or self._has_cpp_annotation(annotated, 'declare_equal_to')
        return gen_hash or gen_equal_to

    def _generate_hash_equal_to(self, obj):
        annotated = obj.type if obj.is_typedef else obj
        is_enum = isinstance(obj, frontend.t_enum)
        gen_hash = is_enum or self._has_cpp_annotation(annotated, 'declare_hash')
        gen_equal_to = is_enum or self._has_cpp_annotation(annotated, 'declare_equal_to')
        if gen_hash or gen_equal_to:
            name = obj.symbolic if obj.is_typedef else obj.name
            full_name = self._namespace_prefix(self._get_namespace()) + name
            with self._types_global.namespace('std').scope:
                if gen_hash:
                    if is_enum:
                        out(('template<> struct hash<typename {t}> '
                             ': public apache::thrift::detail::enum_hash<typename {t}> {{}};')
                            .format(t=full_name))
                    else:
                        out('template<> struct hash<typename ' + full_name + '> {')
                        out('  size_t operator()(const ' + full_name + '&) const;')
                        out("};")

                if gen_equal_to:
                    if is_enum:
                        out(('template<> struct equal_to<typename {t}> '
                             ': public apache::thrift::detail::enum_equal_to<typename {t}> {{}};')
                            .format(t=full_name))
                    else:
                        out('template<> struct equal_to<typename ' + full_name + '> {')
                        out('  bool operator()(const ' + full_name + '&,'
                            'const ' + full_name + '&) const;')
                        out("};")

    def _generate_cpp_struct(self, obj, is_exception=False):
        # We write all of these to the types scope
        scope = self._types_scope
        self._generate_struct_complete(scope, obj, is_exception,
                                       pointers=False,
                                       read=True,
                                       write=True,
                                       swap=True,
                                       result=False,
                                       to_additional=False,
                                       simplejson=True)

        # We're at types scope now
        scope.release()
        with self._types_global.namespace('apache.thrift').scope:
            self._generate_cpp2ops(False, obj, self._types_scope)

        # std::hash and std::equal_to declarations
        self._generate_hash_equal_to(obj)

        # Re-enter types scope, but we can't actually re-enter a scope,
        # so let's recreate it
        scope = self._types_scope = \
                scope.namespace(self._get_namespace()).scope
        scope.acquire()

    def _generate_object(self, obj):
        self._generate_cpp_struct(obj, obj.is_xception)

    _generate_map = {
        frontend.t_typedef: _generate_typedef,
        frontend.t_enum: _generate_enum,
        frontend.t_struct: _generate_object,
        frontend.t_service: _generate_service,
    }

    def _generate(self, what):
        '''This uses a class-static map of (parse_type -> function that
        generates that kind of object), defined above.'''
        # TODO This feels a little hackish. Maybe change this into individual
        # per-type functions, and also change t_generator.generate_program() to
        # call those functions instead of only _generate().
        try:
            gen_func = self._generate_map[what.__class__]
            gen_func(self, what)
        except KeyError:
            print("Warning: Did not generate {}".format(what))

    def _render_const_value_reference(self, type_, value, defining):
        '''Returns representation of rendered const value reference if
        it can be used, None otherwise.'''
        if not (value and value.owner):
            return None

        # Can't reference what we're currently defining.
        if value.owner == defining:
            return None

        # Enum values look like constants of type i32, don't reference them.
        ot = self._get_true_type(value.owner.type)
        if ot.is_base_type and ot.as_base_type.base == t_base.i32:
            return None

        # Make explicit temporary value by copy.
        return '{}({}{}_constants::{}())'.format(
            self._type_name(self._get_true_type(type_)),
            self._namespace_prefix(
                self._get_namespace(value.owner.program)),
            value.owner.program.name,
            value.owner.name)

    def _render_const_value(self, type_, value, literal=False, defining=None,
                            allow_references=True):
        ''' Returns an initializer list rval representing this const
        '''
        # When defining some constant value, try to replace reference to
        # subtrees of the constant with code references to prior definitions.
        if allow_references:
            rendered_reference = self._render_const_value_reference(
                type_, value, defining)
            if rendered_reference is not None:
                return rendered_reference

        t = self._get_true_type(type_)
        if t.is_base_type:
            int32 = lambda x: str(x.integer)
            int64 = lambda x: str(x.integer) + "LL"

            bt = t.as_base_type
            render_string = lambda x: x.string.replace('"', '\\"') if x else ''
            mapping = {
                t_base.string: lambda x: render_string(x) if literal else
                ('apache::thrift::StringTraits< {0}>::'
                 'fromStringLiteral("{1}")').format(
                     self._type_name(t, direct=True), render_string(x)),
                t_base.bool: lambda x: (x.integer > 0 and 'true' or 'false'),
                t_base.byte: lambda x: (
                    "static_cast<int8_t>(" + str(x.integer) + ")"),
                t_base.i16: lambda x: (
                    "static_cast<int16_t>(" + str(x.integer) + ")"),
                t_base.i32: int32,
                t_base.i64: int64,
                t_base.double:
                    lambda x: (x.type == e_cv_type.integer
                                 and str(x.integer)
                                 or str(x.double)),
                t_base.float:
                    lambda x: (x.type == e_cv_type.integer
                                 and str(x.integer)
                                 or str(x.double))
            }
            if not bt.base in mapping:
                # TODO replace bt.t_base_name(bt.base) with bt.base.LABEL
                # However, (afaik) boost::python apparently doesn't support
                # reverse label lookups for enums
                raise CompilerError("No const of base type " + \
                                    bt.t_base_name(bt.base))
            return mapping[bt.base](value)
        elif t.is_enum:
            name = self._type_name(t)
            integer = value.integer if value else 0
            const = t.find_value(integer)
            if const:
                return '{0}::{1}'.format(name, const.name)
            else:
                return 'static_cast<{0}>({1})'.format(name, integer)
        elif t.is_struct or t.is_xception:
            value_map = {}
            for k, v in value.map.items():
                value_map[k.string] = v
            if not value_map:
                return ('{0}()'.format(self._type_name(t))
                        if not defining else None)
            fields = filter(self._should_generate_field, t.as_struct.members)
            out_list = []
            for field in fields:
                if field.name in value_map:
                    out_list.append(
                        '::apache::thrift::detail::wrap_argument<{0}>({1})'
                        .format(field.key, self._render_const_value(
                            field.type, value_map[field.name],
                            allow_references=allow_references)))
            return '{0}({1})'.format(self._type_name(t), ', '.join(out_list))
        elif t.is_map:
            outlist = []
            for key, value in value.map.items():
                key_render = self._render_const_value(
                    t.key_type, key, allow_references=allow_references)
                value_render = self._render_const_value(
                    t.value_type, value, allow_references=allow_references)
                outlist.append('{{{0}, {1}}}'.format(key_render, value_render))
            out_prefix = 'std::initializer_list<std::pair<const {0}, {1}>>'\
                .format(self._type_name(self._get_true_type(t.key_type)),
                        self._type_name(self._get_true_type(t.value_type)))
            if not outlist:
                return out_prefix + '{}' if not defining else None
            return out_prefix + '{' + ',\n'.join(outlist) + '}'
        elif t.is_list:
            out_prefix = 'std::initializer_list<' + \
                self._type_name(t.as_list.elem_type) + '>'
            outlist = []
            for item in value.list:
                field_render = self._render_const_value(
                    t.as_list.elem_type, item,
                    allow_references=allow_references)
                outlist.append(field_render)
            if not outlist:
                return out_prefix + '{}' if not defining else None
            return out_prefix + '{' + ',\n'.join(outlist) + '}'
        elif t.is_set:
            out_prefix = 'std::initializer_list<' + \
                self._type_name(t.as_set.elem_type) + '>'
            outlist = []
            for item in value.list:
                field_render = self._render_const_value(
                    t.as_set.elem_type, item,
                    allow_references=allow_references)
                outlist.append(field_render)
            if not outlist:
                return out_prefix + '{}' if not defining else None
            return out_prefix + '{' + ',\n'.join(outlist) + '}'
        else:
            raise TypeError('INVALID TYPE IN print_const_definition: ' + t.name)

    def _generate_layouts(self, objects):
        if not self.flag_frozen2:
            return
        context = self._make_context(self._program.name + '_layouts')
        s = get_global_scope(CppPrimitiveFactory, context)
        s('#include <thrift/lib/cpp2/frozen/Frozen.h>')
        s('#include "{0}"'.format(self._with_include_prefix(self._program,
            self._program.name + '_types.h')))
        # Include other layouts
        for inc in self._program.includes:
            s('#include "{0}_layouts.h"'
             .format(self._with_include_prefix(inc, inc.name)))
        with s.namespace('apache.thrift.frozen').scope:
            for obj in objects:
                self._generate_frozen_layout(obj, out())
        s.release()

    def _generate_consts(self, constants):
        name = self._program.name
        # build the const scope
        context = self._make_context(
            self._program.name + '_constants',
            is_types_context=True)
        sg = get_global_scope(CppPrimitiveFactory, context)
        # Include the types header
        sg('#include "{0}"'.format(self._with_include_prefix(self._program,
            self._program.name + '_types.h')))
        sg('#include <thrift/lib/cpp2/protocol/Protocol.h>')
        if constants:
            # Include other constant includes that might be required by
            # const values rendered with allow_references=True.
            for inc in self._program.includes:
                if not inc.consts:
                    continue
                print >>context.impl, ('#include "{0}_constants.h"'.format(
                    self._with_include_prefix(inc, inc.name)))
        print >>context.impl, '#include <folly/Indestructible.h>\n'

        # Open namespace
        sns = sg.namespace(self._get_namespace()).scope

        # DECLARATION
        s = sns.cls('struct {0}_constants'.format(name)).scope
        with s:
            out('\n')
            # Default constructor
            for c in constants:
                inlined = c.type.is_base_type or c.type.is_enum
                value = self._render_const_value(
                    c.type, c.value, literal=inlined, defining=c,
                    allow_references=not inlined)
                if inlined:
                    if c.type.is_string:
                        s('// consider using folly::StringPiece instead of '
                            + 'std::string whenever possible')
                        s('// to referencing this statically allocated string'
                            + ' constant, in order to ')
                        s('// prevent unnecessary allocations')
                    s.defn(('static constexpr {0} const {{name}}_ = {1}{2}'
                        + '{1};').format(
                            'char const *' if c.type.is_string
                                else self._type_name(c.type),
                            '"' if c.type.is_string else '',
                            value if value is not None else "{}"
                        ), name=c.name, in_header=True)
                    sns.impl('constexpr {0} const {1}_constants::{2}_;\n\n'
                      .format('char const *' if c.type.is_string else
                        self._type_name(c.type), name, c.name))

                b = s.defn(
                    '{0}{1} {2}{{name}}()'.format(
                        'constexpr ' if inlined else '',
                        'char const *' if c.type.is_string
                        else self._type_name(c.type),
                        '' if inlined else 'const& '),
                    name=c.name,
                    in_header=inlined,
                    modifiers='static').scope
                with b:

                    if inlined:
                        b('return {0}_;'.format(c.name))
                    else:
                        instance_args = (
                            '({0})'.format(value) if value is not None else '')
                        b('static folly::Indestructible<{0}> const instance{1};'
                                .format(self._type_name(c.type), instance_args))
                        b('return *instance;')
                out('\n')

        sns.release()  # namespace

        sg.release()   # global scope

    # ==========================================================================
    # FATAL REFLECTION CODE - begin
    # ==========================================================================

    def _generate_fatal(self, program):
        name = self._program.name
        ns = self._get_original_namespace()
        self.fatal_detail_ns = 'thrift_fatal_impl_detail'
        context = self._make_context(
            name + '_fatal', tcc=False, impl=False)
        context.omit_include = True
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include <thrift/lib/cpp2/fatal/reflection.h>')
            sg()
            sg('#include <fatal/type/list.h>')
            sg('#include <fatal/type/pair.h>')
            sg('#include <fatal/type/sequence.h>')
            sg()
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_types.h')))

            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    self._generate_fatal_impl(sns, program)
            else:
                self._generate_fatal_impl(sg, program)
            sg('#include "{0}_fatal_types.h"'.format(
                self._with_include_prefix(self._program, name)))

    def _generate_fatal_impl(self, sns, program):
        name = self._program.name
        self.fatal_tags_class = '{0}_tags'.format(name)
        self.fatal_tag = '{0}::module'.format(self.fatal_tags_class)
        safe_ns = self._get_namespace().replace('.', '_')

        str_class = '{0}_{1}__unique_strings_list'.format(safe_ns, name)
        self.fatal_str_map_id = '{0}::{1}'.format(
            self.fatal_detail_ns, str_class)
        self.fatal_str_map = {}
        self.fatal_str_uid = []
        self._fatal_type_dependencies = None

        name_id = self._set_fatal_string(name)

        order = ['language', 'enum', 'union', 'struct', 'constant', 'service']
        items = {}
        items['language'] = self._generate_fatal_language(program)
        items['enum'] = self._generate_fatal_enum(program)
        items['union'] = self._generate_fatal_union(program)
        items['struct'] = self._generate_fatal_struct(program)
        items['constant'] = self._generate_fatal_constant(program)
        items['service'] = self._generate_fatal_service(program)

        # Combo include: types
        context_cmb_types = self._make_context(
            name + '_fatal_types', tcc=False, impl=False, is_types_context=True)
        with get_global_scope(CppPrimitiveFactory, context_cmb_types) as sg:
            for dep in self._get_fatal_type_dependencies():
                sg('#include  "{0}_fatal_types.h"'
                   .format(self._with_include_prefix(dep, dep.name)))
            sg()
            for what in ['enum', 'union', 'struct']:
                sg('#include "{0}"'.format(self._with_include_prefix(
                    self._program, name + '_fatal_' + what + '.h')))

        # Combo include: all
        context_cmb_all = self._make_context(
            name + '_fatal_all', tcc=False, impl=False)
        with get_global_scope(CppPrimitiveFactory, context_cmb_all) as sg:
            for dep in self._get_fatal_type_dependencies():
                sg('#include  "{0}_fatal_all.h"'
                   .format(self._with_include_prefix(dep, dep.name)))
            sg()
            sg('#include "{0}_types.h"'.format(
                self._with_include_prefix(self._program, name)))
            sg()
            for what in ['types', 'constant', 'service']:
                sg('#include "{0}"'.format(self._with_include_prefix(
                    self._program, name + '_fatal_' + what + '.h')))

        # Unique Compile-time Strings
        with sns.namespace(self.fatal_detail_ns).scope as detail:
            with detail.cls('struct {0}'.format(str_class)).scope as cstr:
                for i in self.fatal_str_map:
                    cstr('using {0} = {1};'.format(
                        self.fatal_str_map[i],
                        self._render_fatal_string(i)))

        # Metadata tags
        sns('class {0}_tags {1}'.format(name, '{')).scope
        nname = {}
        for o in order:
            nname[o] = '{0}_{1}__unique_{2}s_list'.format(safe_ns, name, o)
            eorder = items[o][0]
            entries = items[o][1]
            sns('  struct {0} {1}'.format(nname[o], '{'))
            for i in eorder:
                if type(entries) == list:
                    sns('    using {0} = {1};'.format(
                        self._get_fatal_string_short_id(i[0]), i[1]))
                elif type(entries) == dict:
                    sns('    using {0} = {1};'.format(
                        self._get_fatal_string_short_id(entries[i][0]),
                        entries[i][1]))
            sns('  };')
            sns()
        sns('public:')
        sns('  struct module {0}'.format('{};'))
        sns()
        for o in order:
            sns('  using {0}s = {1};'.format(o, nname[o]))
        sns()
        sns('  using strings = {0};'.format(self.fatal_str_map_id))
        sns('};')
        sns()

        # Metadata registration
        sns('THRIFT_REGISTER_REFLECTION_METADATA(')
        sns('  {0},'.format(self.fatal_tag))
        sns('  {0},'.format(name_id))
        for item_idx, o in enumerate(order):
            eorder = items[o][0]
            entries = items[o][1]
            sns('  // {0}s'.format(o))
            sns('  ::fatal::list<')
            for idx, i in enumerate(eorder):
                if type(entries) == list:
                    sns('    {0}{1}'.format(
                        i[2], ',' if idx + 1 < len(entries) else ''))
                elif type(entries) == dict:
                    sns('    ::fatal::pair<{0}, {1}>{2}'
                        .format(i, entries[i][2],
                                ',' if idx + 1 < len(entries) else ''))
            sns('  >{0}'.format(',' if item_idx + 1 < len(items) else ''))
        sns(');')

    def _get_fatal_string_short_id(self, s):
        return self.fatal_str_map[s]

    def _get_fatal_string_id(self, s):
        return '{0}::{1}'.format(
            self.fatal_str_map_id, self._get_fatal_string_short_id(s))

    def _set_fatal_string(self, s):
        if s not in self.fatal_str_map:
            identifier = s
            if len(identifier) == 0:
                identifier = "empty"
            else:
                if identifier[0] not in string.ascii_letters + '_':
                    identifier = "s_" + identifier
                identifier = re.sub(r'[\W\.:]', '_', s)

            uid = identifier
            uid_round = 0
            while uid in self.fatal_str_uid:
                uid = "{0}_{1}".format(identifier, uid_round)
                uid_round = uid_round + 1
            self.fatal_str_uid.append(uid)
            self.fatal_str_map[s] = uid
        return self._get_fatal_string_id(s)

    def _get_original_namespace(self):
        return self._get_namespace()

    def _get_scoped_original_namespace(self):
        ns = self._get_original_namespace()
        if len(ns) > 0:
            return '::{0}'.format(ns.replace('.', '::'))
        return ''

    def _render_fatal_string(self, s):
        result = "::fatal::sequence<char"
        substitutions = {
            '\0': '\\0',
            '\n': '\\n',
            '\r': '\\r',
            '\t': '\\t',
            '\'': '\\\'',
            '\\': '\\\\',
        }
        for i in s:
            result += ", '{0}'".format(substitutions.get(i, i))
        result += ">"
        return result

    # this is an attempt to be more conservative on what dependencies require
    # to enable compile-time reflection than `program.includes`
    def _get_fatal_type_dependencies(self):
        if self._fatal_type_dependencies is None:
            dependencies = set()
            for struct in self._program.structs:
                for member in struct.members:
                    def traverse(type):
                        type = self._get_true_type(type)
                        if type.is_struct:
                            yield type
                        if type.is_enum:
                            yield type
                        if type.is_list:
                            for t in traverse(type.elem_type):
                                yield t
                        if type.is_set:
                            for t in traverse(type.elem_type):
                                yield t
                        if type.is_map:
                            for t in traverse(type.key_type):
                                yield t
                            for t in traverse(type.value_type):
                                yield t
                    dependencies.update(
                        t.program
                        for t in traverse(member.type)
                        if t.program is not None and t.program != self._program
                    )
            self._fatal_type_dependencies = (
                list(sorted(dependencies, key=lambda d: d.path)))
        return self._fatal_type_dependencies

    def _render_fatal_type_class(self, ttype):
        while ttype.is_typedef:
            ttype = ttype.type

        if ttype.is_void:
            return '::apache::thrift::type_class::nothing'
        elif ttype.is_base_type and ttype.as_base_type.is_binary:
            return '::apache::thrift::type_class::binary'
        elif ttype.is_string:
            return '::apache::thrift::type_class::string'
        elif ttype.is_floating_point:
            return '::apache::thrift::type_class::floating_point'
        elif ttype.is_base_type:
            return '::apache::thrift::type_class::integral'
        elif ttype.is_enum:
            return '::apache::thrift::type_class::enumeration'
        elif ttype.is_list:
            return '::apache::thrift::type_class::list<{0}>'.format(
                self._render_fatal_type_class(ttype.as_list.elem_type))
        elif ttype.is_map:
            return '::apache::thrift::type_class::map<{0}, {1}>'.format(
                self._render_fatal_type_class(ttype.as_map.key_type),
                self._render_fatal_type_class(ttype.as_map.value_type))
        elif ttype.is_set:
            return '::apache::thrift::type_class::set<{0}>'.format(
                self._render_fatal_type_class(ttype.as_set.elem_type))
        elif ttype.is_struct:
            if ttype.as_struct.is_union:
                return '::apache::thrift::type_class::variant'
            else:
                return '::apache::thrift::type_class::structure'

        return '::apache::thrift::type_class::unknown'

    def _render_fatal_required_qualifier(self, required):
        if required == e_req.required:
            return 'required'
        elif required == e_req.optional:
            return 'optional'
        else:
            assert required == e_req.opt_in_req_out, \
                "unknown required qualifier"
            return 'required_of_writer'

    def _render_fatal_structured_annotation(self, what, out, indent, comma):
        t = type(what)
        comma = ',' if comma else ''

        if t == bool:
            out('{0}::std::{1}_type{2}'.format(
                indent, 'true' if what else 'false', comma))
        elif t == int:
            out('{0}::std::integral_constant< ::std::{1}max_t, {2}>{3}'.format(
                indent, 'int' if what < 0 else 'uint', what, comma))
        elif t == list or t == set:
            out('{0}::fatal::list<'.format(indent))
            for i, k in enumerate(what):
                self._render_fatal_structured_annotation(k, out, indent + '  ',
                                                         i + 1 < len(what))
            out('{0}>{1}'.format(indent, comma))
        elif t == dict:
            out('{0}::fatal::list<'.format(indent))
            for i, (k, v) in enumerate(sorted(what.iteritems())):
                out('{0}  ::fatal::pair<'.format(indent))
                self._render_fatal_structured_annotation(k, out,
                                                         indent + '    ', True)
                self._render_fatal_structured_annotation(v, out,
                                                         indent + '    ', False)
                out('{0}  >{1}'.format(indent,
                                       ',' if i + 1 < len(what) else ''))
            out('{0}>{1}'.format(indent, comma))
        else:  # treat as string
            out('{0}{1}{2}'.format(indent, self._render_fatal_string(str(what)),
                comma))

    def _render_fatal_annotations(self, annotations, class_name, scope):
        clsnmkeys = '{0}__unique_annotations_keys'.format(class_name)
        clsnmvalues = '{0}__unique_annotations_values'.format(class_name)
        annotation_keys = []
        black_list = [
            'cpp.methods',
            'cpp.ref',
            'cpp.ref_type',
            'cpp.template',
            'cpp.type',
            'cpp2.methods',
            'cpp2.ref',
            'cpp2.ref_type',
            'cpp2.template',
            'cpp2.type',
        ]
        if annotations is not None:
            for i in sorted(annotations.keys()):
                if i not in black_list:
                    annotation_keys.append(i)
        with scope.cls('class {0}'.format(class_name)).scope as aclass:
            if len(annotation_keys) > 0:
                with scope.cls('struct {0}'.format(clsnmkeys)).scope as akeys:
                    for idx, i in enumerate(annotation_keys):
                        full_id = self._set_fatal_string(i)
                        short_id = self._get_fatal_string_short_id(i)
                        akeys('using {0} = {1};'.format(short_id, full_id))
                with scope.cls('struct {0}'.format(clsnmvalues)).scope as aval:
                    for idx, i in enumerate(annotation_keys):
                        aval('using {0} = {1};'.format(
                            self._get_fatal_string_short_id(i),
                            self._render_fatal_string(annotations[i])))
                aclass()
            aclass('public:')
            if len(annotation_keys) > 0:
                aclass('using keys = {0};'.format(clsnmkeys))
                aclass('using values = {0};'.format(clsnmvalues))
            else:
                aclass('using keys = void;')
                aclass('using values = void;')
            aclass('using map = ::fatal::list<')
            import json
            for idx, i in enumerate(annotation_keys):
                structured = None
                try:
                    structured = json.loads(annotations[i])
                except ValueError:
                    pass

                identifier = self._get_fatal_string_short_id(i)
                aclass('  ::apache::thrift::annotation<')
                aclass('    keys::{0},'.format(identifier))
                aclass('    values::{0}{1}'
                       .format(identifier, '' if structured is None else ','))
                if structured is not None:
                    self._render_fatal_structured_annotation(
                        structured, aclass, '    ', False)
                aclass('  >{0}'
                       .format(',' if idx + 1 < len(annotation_keys) else ''))
            aclass('>;')
        return class_name

    def _render_fatal_type_common_metadata(self, annotations_class, legacyid,
                                           scope, indent, ns_prefix=''):
        scope('{0}::apache::thrift::detail::type_common_metadata_impl<'.format(
            indent))
        scope('{0}  {1}{2},'.format(indent, ns_prefix, self.fatal_tag))
        scope('{0}  ::apache::thrift::reflected_annotations<{1}>,'
            .format(indent, annotations_class))
        scope('{0}  static_cast<::apache::thrift::legacy_type_id_t>({1}ull)'
            .format(indent, legacyid))
        scope('{0}>'.format(indent))

    def _generate_fatal_language(self, program):
        replacement = {'cpp': '::', 'cpp2': '::', 'php': '_'}
        order = []
        result = {}
        for i in program.namespaces:
            language = i.key()
            ns = self._program.get_namespace(language)
            if language in replacement:
                ns = ns.replace('.', replacement[language])
            lang_id = self._set_fatal_string(language)
            result[lang_id] = (language, lang_id, self._set_fatal_string(ns))
            order.append(lang_id)
        return (order, result)

    def _generate_fatal_enum(self, program):
        name = self._program.name
        ns = self._get_original_namespace()

        context = self._make_context(
            name + '_fatal_enum', tcc=False, impl=False, is_types_context=True)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_types.h')))

            sg('#include "{0}"'.format(self._fatal_header()))

            sg()
            sg('#include <fatal/type/enum.h>')
            sg()
            sg('#include <type_traits>')
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_enum_impl(sns, program)
            else:
                return self._generate_fatal_enum_impl(sg, program)

    def _generate_fatal_enum_impl(self, sns, program):
        result = {}
        order = []
        for i in program.enums:
            self._generate_fatal_enum_traits(i.name, i.name, i.constants, sns,
                i.annotations, i.type_id)
            string_ref = self._set_fatal_string(i.name)
            result[i.name] = (i.name, string_ref, string_ref)
            order.append(i.name)
        return (order, result)

    def _generate_fatal_enum_traits(self, name, scoped_name, members, scope,
      annotations, legacyid, guaranteed_unique=False):
        scoped_ns = self._get_scoped_original_namespace()
        traits_name = '{0}_enum_traits'.format(scoped_name.replace('::', '_'))
        with scope.namespace(self.fatal_detail_ns).scope as detail:
            with detail.cls('struct {0}'.format(traits_name)).scope as t:
                t('using type = {0}::{1};'.format(scoped_ns, scoped_name))
                t('using name = {0};'.format(self._set_fatal_string(name)))
                t()
                strcls = '{0}__struct_unique_strings_list'.format(name)
                with t.cls('struct {0}'.format(strcls)):
                    for i in members:
                        cseq = self._set_fatal_string(i.name)
                        t('using {0} = {1};'.format(i.name, cseq))
                t()
                mbmclsprefix = '{0}__struct_enum_members_'.format(name)
                for i in members:
                    with t.cls('struct {0}{1}'.format(mbmclsprefix, i.name)):
                        t('using name = {0}::{1};'.format(strcls, i.name))
                        t(('using value = std::integral_constant<type'
                            ', type::{0}>;').format(i.name))
                        self._render_fatal_annotations(
                            i.annotations, 'annotations', t)
                t()
                mbmcls = '{0}__struct_enum_members'.format(name)
                with t.cls('struct {0}'.format(mbmcls)):
                    for i in members:
                        t('using {0} = {1}{0};'.format(i.name, mbmclsprefix))
                t()
                t('using member = {0};'.format(mbmcls))
                t()
                t('using fields = ::fatal::list<')
                for idx, i in enumerate(members):
                    t('    member::{0}{1}'
                        .format(i.name, ',' if idx + 1 < len(members) else ''))
                t('>;')
                t()
                self._render_fatal_annotations(annotations, 'annotations', t)
                t()
                t('static char const *to_string(type e, char const *fallback)'
                    + ' {0}'.format('{'))
                t('  switch (e) {')
                member_names = []
                member_values = []
                for i in members:
                    if guaranteed_unique or i.value not in member_values:
                        member_names.append(i.name)
                        member_values.append(i.value)
                for i in member_names:
                    t('    case type::{0}: return "{0}";'.format(i))
                t('    default: return fallback;')
                t('  }')
                t('}')
        scope()
        scope('FATAL_REGISTER_ENUM_TRAITS(')
        scope('  {0}::{1}::{2},'.format(
            scoped_ns, self.fatal_detail_ns, traits_name))
        self._render_fatal_type_common_metadata(
            '{0}::{1}::{2}::annotations'.format(
                scoped_ns, self.fatal_detail_ns, traits_name),
            legacyid, scope, '  ')
        scope(');')
        return traits_name

    def _generate_fatal_union(self, program):
        name = self._program.name
        ns = self._get_original_namespace()

        context = self._make_context(
            name + '_fatal_union', tcc=False, impl=False, is_types_context=True)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            for dep in self._get_fatal_type_dependencies():
                sg('#include  "{0}_fatal_types.h"'
                   .format(self._with_include_prefix(dep, dep.name)))
            sg()
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_types.h')))

            sg('#include "{0}"'.format(self._fatal_header()))
            sg()
            sg('#include <fatal/type/enum.h>')
            sg('#include <fatal/type/variant_traits.h>')
            sg()
            sg('#include <type_traits>')
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_union_impl(sns, program)
            else:
                return self._generate_fatal_union_impl(sg, program)

    def _generate_fatal_union_impl(self, sns, program):
        scoped_ns = self._get_scoped_original_namespace()
        result = {}
        order = []
        data = {}
        for i in program.structs:
            if not i.is_union:
                continue
            self._generate_fatal_enum_traits(
                'Type', '{0}::Type'.format(i.name),
                i.members, sns, None, 0, True)
            sns()
            string_ref = self._set_fatal_string(i.name)
            result[i.name] = (i.name, string_ref, string_ref)
            order.append(i.name)
            data[i.name] = [None, i.type_id]
            with sns.namespace(self.fatal_detail_ns).scope as detail:
                data[i.name][0] = self._generate_fatal_union_traits(
                    i, detail, i.annotations)
        if len(data) > 0:
            sns()
        for i in order:
            traits_class = '{0}::{1}::{2}'.format(
                scoped_ns, self.fatal_detail_ns, data[i][0])
            sns('FATAL_REGISTER_VARIANT_TRAITS(')
            sns('  {0},'.format(traits_class))
            self._render_fatal_type_common_metadata(
                '{0}::annotations'.format(traits_class), data[i][1], sns, '  ')
            sns(');')
        return (order, result)

    def _generate_fatal_union_traits(self, union, scope, annotations):
        scoped_ns = self._get_scoped_original_namespace()
        name = '{0}_variant_traits'.format(union.name)
        type_name = '{0}::{1}'.format(scoped_ns, union.name)
        with scope.cls('class {0}'.format(name)).scope as t:
            idscls = '{0}__struct_unique_identifiers_list'.format(union.name)
            with t.cls('struct {0}'.format(idscls)):
                for i in union.members:
                    t(('using {0} = std::integral_constant<{1}::Type, {1}::Type'
                       '::{0}>;').format(i.name, type_name))
            t()
            getcls = '{0}__struct_unique_getters_list'.format(union.name)
            with t.cls('struct {0}'.format(getcls)):
                for i in union.members:
                    self._generate_fatal_union_traits_getter(union, i, t)
            t()
            setcls = '{0}__struct_unique_setters_list'.format(union.name)
            with t.cls('struct {0}'.format(setcls)):
                for i in union.members:
                    self._generate_fatal_union_traits_setter(union, i, t)
            t()
            t('public:')
            t('using type = {0};'.format(type_name))
            t('using name = {0};'.format(self._set_fatal_string(union.name)))
            t('using id = type::Type;')
            t('using ids = {0};'.format(idscls))
            t('using descriptors = ::fatal::list<')
            for idx, i in enumerate(union.members):
                t('  ::fatal::variant_member_descriptor<')
                t('    {0},'.format(self._type_name(i.type)))
                t('    {0}::{1},'.format(idscls, i.name))
                t('    {0}::{1},'.format(getcls, i.name))
                t('    {0}::{1},'.format(setcls, i.name))
                t('    ::apache::thrift::reflected_variant_member_metadata<')
                t('      {0},'.format(self._get_fatal_string_id(i.name)))
                t('      {0},'.format(i.key))
                t('      {0}'.format(self._render_fatal_type_class(i.type)))
                t('    >')
                t('  >{0}'.format(',' if idx + 1 < len(union.members) else ''))
            t('>;')
            t()
            self._render_fatal_annotations(annotations, 'annotations', t)
            t()
            t('static id get_id(type const &variant) {0}'.format('{'))
            t('  return variant.getType();')
            t('}')
            t()
            t('static bool empty(type const &variant) {0}'.format('{'))
            t('  return variant.getType() == id::__EMPTY__;')
            t('}')
            t()
            t('static void clear(type &variant) {0}'.format('{'))
            t('  return variant.__clear();')
            t('}')
        return name

    def _generate_fatal_union_traits_getter(self, union, field, scope):
        with scope.cls('struct {0}'.format(field.name)):
            scope('decltype(auto) operator ()({0} const &variant) const {{'
                .format(union.name))
            scope('  return variant.get_{0}();'.format(field.name))
            scope('}')
            scope('decltype(auto) operator ()({0} &variant) const {{'
                .format(union.name))
            scope('  return variant.mutable_{0}();'.format(field.name))
            scope('}')
            scope('decltype(auto) operator ()({0} &&variant) const {{'
                .format(union.name))
            scope('  return std::move(variant).move_{0}();'.format(field.name))
            scope('}')

    def _generate_fatal_union_traits_setter(self, union, field, scope):
        with scope.cls('struct {0}'.format(field.name)):
            scope('template <typename... Args>')
            scope('decltype(auto) operator ()({0} &variant, Args &&...args) const {{'
                .format(union.name))
            scope('  return variant.set_{0}(std::forward<Args>(args)...);'
                .format(field.name))
            scope('}')

    def _fatal_header(self):
        return "{0}_fatal.h".format(self._with_include_prefix(
            self._program, self._program.name))

    def _generate_fatal_struct(self, program):
        name = self._program.name
        ns = self._get_original_namespace()
        context = self._make_context(
            name + '_fatal_struct', tcc=False, impl=False, is_types_context=True)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            for dep in self._get_fatal_type_dependencies():
                sg('#include  "{0}_fatal_types.h"'
                   .format(self._with_include_prefix(dep, dep.name)))
            sg()
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_types.h')))
            sg()
            sg('#include "{0}"'.format(self._fatal_header()))
            sg()
            sg('#include <fatal/type/traits.h>')
            sg('#include <fatal/type/list.h>')
            sg()
            result = None
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    result = self._generate_fatal_struct_impl(sns, program)
            else:
                result = self._generate_fatal_struct_impl(sg, program)
            return self._generate_fatal_typedef_impl(
                sg, program, result[0], result[1], result[2])

    def _generate_fatal_struct_impl(self, sns, program):
        name = self._program.name
        safe_ns = self._get_namespace().replace('.', '_')
        dtmclsprefix = '{0}_{1}__struct_unique_data_member_getters_list'.format(
            safe_ns, name)
        indclsprefix = '{0}_{1}__struct_unique_indirections_list'.format(
            safe_ns, name)
        mpdclsprefix = '{0}_{1}__struct_unique_member_pod_list'.format(
            safe_ns, name)
        annclsprefix = '{0}_{1}__struct_unique_annotations'.format(
            safe_ns, name)
        mnfclsprefix = '{0}_{1}__struct_unique_member_info_list'.format(
            safe_ns, name)

        structs = []
        for struct in program.structs:
            structs.append(struct)
        for exception in program.exceptions:
            structs.append(exception)

        aliases = []
        for i in program.typedefs:
            alias = i.type
            if alias.is_typedef and 'cpp.type' in alias.annotations:
                ttype = i.type
                while ttype.is_typedef:
                    ttype = ttype.type
                if not (ttype.is_struct or ttype.is_xception):
                    continue
                ns_prefix = self._namespace_prefix(
                    self._get_original_namespace())
                aliases.append((i, alias, ttype, ns_prefix))

        with sns.namespace(self.fatal_detail_ns).scope as detail:
            members = []
            for i in structs:
                if i.is_union:
                    continue
                self._set_fatal_string(i.name)
                for m in i.members:
                    self._set_fatal_string(m.name)
                    if m.name not in members:
                        members.append(m.name)
            with detail.cls('struct {0}'.format(dtmclsprefix)).scope as dtm:
                for i in members:
                    dtm('FATAL_DATA_MEMBER_GETTER({0}, {0});'.format(i))
            for i in structs:
                if i.is_union:
                    continue
                if any(self._type_access_suffix(m.type) for m in i.members):
                    with detail.cls('struct {0}_{1}'.format(
                            i.name, indclsprefix)).scope as ind:
                        for m in i.members:
                            note = self._type_access_suffix(m.type)
                            if not note:
                                continue
                            with ind.cls('struct {0}'.format(
                                    m.name)).scope as ind:
                                out('template <typename T{0}>\n'
                                    'static auto val(T{0} &&{0}) {{\n'
                                    '  return std::forward<T{0}>({0}){1};\n'
                                    '}}'.format('__thrift__arg__', note))
                                out('template <typename T{0}>\n'
                                    'static auto &&ref(T{0} &&{0}) {{\n'
                                    '  return std::forward<T{0}>({0}){1};\n'
                                    '}}'.format('__thrift__arg__', note))
            with detail.cls('struct {0}'.format(mpdclsprefix)).scope as mpd:
                pod_arg = 'T_{0}_{1}_struct_member_pod'.format(safe_ns, name)
                for i in members:
                    mpd('template <typename {0}>'.format(pod_arg))
                    with detail.cls('struct {0}_{1}_struct_member_pod_{2}'
                      .format(safe_ns, name, i)).scope as pod:
                        pod('{0} {1};'.format(pod_arg, i))
            for i in structs:
                if i.is_union:
                    continue
                with detail.cls('class {0}_{1}'.format(
                        i.name, annclsprefix)).scope as cann:
                    members_class = '{0}_{1}_members'.format(
                        i.name, annclsprefix)
                    with cann.cls('class {0}'.format(
                            members_class)).scope as cmann:
                        for m in i.members:
                            self._render_fatal_annotations(
                                m.annotations, '{0}_{1}'.format(
                                    members_class, m.name), cmann)
                        cmann('public:')
                        for m in i.members:
                            cmann(('using {0} = ::apache::thrift::'
                                'reflected_annotations<{1}_{0}>;').format(
                                    m.name, members_class))
                    self._render_fatal_annotations(i.annotations,
                        'annotations', cann)
                    cann('public:')
                    cann('using keys = annotations::keys;')
                    cann('using values = annotations::values;')
                    cann('using map = annotations::map;')
                    cann('using members = {0};'.format(members_class))
                with detail.cls(('struct {0}_{1}').format(
                        i.name, mnfclsprefix)).scope as cmnf:
                    for m in i.members:
                        cmnf(('using {0} = '
                            '::apache::thrift::reflected_struct_data_member<')
                            .format(m.name))
                        cmnf('')
                        cmnf('  {0},'.format(self._get_fatal_string_id(m.name)))
                        cmnf('  {0},'.format(self._type_name(m.type)))
                        cmnf('  {0},'.format(m.key))
                        cmnf('  ::apache::thrift::optionality::{0},'.format(
                            self._render_fatal_required_qualifier(m.req)))
                        if self._type_access_suffix(m.type):
                            cmnf('  ::apache::thrift::detail::'
                                 'chained_data_member_getter<')
                            cmnf('    {0}::{1}::{2},'.format(
                                self.fatal_detail_ns, dtmclsprefix, m.name))
                            cmnf('    ::apache::thrift::detail::'
                                 'reflection_indirection_getter<')
                            cmnf('      {0}::{1}_{2}::{3}'.format(
                                self.fatal_detail_ns,
                                i.name,
                                indclsprefix,
                                m.name))
                            cmnf('    >')
                            cmnf('  >,')
                        else:
                            cmnf('  {0}::{1}::{2},'.format(
                                self.fatal_detail_ns, dtmclsprefix, m.name))
                        cmnf('  {0},'.format(
                            self._render_fatal_type_class(m.type)))
                        cmnf('  {0}::{1}::{2}_{3}_struct_member_pod_{4},'
                            .format(self.fatal_detail_ns, mpdclsprefix,
                                safe_ns, name, m.name))
                        cmnf('  ::apache::thrift::reflected_annotations<'
                             '{0}::{1}_{2}::members::{3}>,'.format(
                                self.fatal_detail_ns, i.name, annclsprefix,
                                m.name))
                        # Owner
                        cmnf('  {0},'.format(i.name))
                        # HasIsSet
                        cmnf('  {0}'.format(
                            'true' if self._has_isset(m) else 'false'))
                        cmnf('>;')
            for i, alias, ttype, ns_prefix in aliases:
                self._set_fatal_string(i.symbolic)
                with detail.cls(('struct {0}_{1}').format(
                        i.symbolic, mnfclsprefix)).scope as cmnf:
                    for m in ttype.members:
                        cmnf(('using {0} = '
                              '::apache::thrift::reflected_struct_data_member<')
                              .format(m.name))
                        cmnf('')
                        cmnf('  {0},'.format(self._get_fatal_string_id(m.name)))
                        cmnf('  decltype(static_cast<{0} *>(nullptr)->{1}),'
                             .format(i.symbolic, m.name))
                        cmnf('  {0},'.format(m.key))
                        cmnf('  ::apache::thrift::optionality::required,')
                        cmnf('  {0}::{1}::{2},'.format(
                             self.fatal_detail_ns, dtmclsprefix, m.name))
                        cmnf('  {0},'.format(
                             self._render_fatal_type_class(m.type)))
                        cmnf('  {0}::{1}::{2}_{3}_struct_member_pod_{4},'
                             .format(self.fatal_detail_ns, mpdclsprefix,
                                     safe_ns, name, m.name))
                        cmnf('  ::apache::thrift::reflected_annotations<'
                             '{0}::{1}_{2}::members::{3}>,'
                             .format(self.fatal_detail_ns, ttype.name,
                                     annclsprefix, m.name))
                        # Owner
                        cmnf('  {0},'.format(i.symbolic))
                        # HasIsSet
                        cmnf('  false')
                        cmnf('>;')

        def render_member_list(type_name, member_list):
            sns('  ::fatal::list<')
            for midx, m in enumerate(member_list):
                sns('      {0}::{1}_{2}::{3}{4}'.format(
                    self.fatal_detail_ns, type_name, mnfclsprefix, m.name,
                    ',' if midx + 1 < len(member_list) else ''))
            sns('  >,')

        result = {}
        order = []
        for i in structs:
            if i.is_union:
                continue
            string_ref = self._set_fatal_string(i.name)
            result[i.name] = (i.name, string_ref, string_ref)
            order.append(i.name)
            sns('THRIFT_REGISTER_STRUCT_TRAITS(')
            # Struct
            sns('  {0},'.format(i.name))
            # Name
            sns('  {0},'.format(self._get_fatal_string_id(i.name)))
            # MembersInfo
            sns('  {0}::{1}_{2},'.format(
                self.fatal_detail_ns, i.name, mnfclsprefix))
            # Info
            sns('  ::fatal::list<')
            annclsnm = '{0}::{1}_{2}'.format(
                self.fatal_detail_ns, i.name, annclsprefix)
            for midx, m in enumerate(i.members):
                sns('      {0}::{1}_{2}::{3}{4}'.format(
                    self.fatal_detail_ns, i.name, mnfclsprefix, m.name,
                    ',' if midx + 1 < len(i.members) else ''))
            sns('  >,')
            # MembersAnnotations
            sns('  {0}::members,'.format(annclsnm))

            # Metadata
            self._render_fatal_type_common_metadata(
                annclsnm, i.type_id, sns, '  ')
            sns(');')

        return (aliases, order, result)

    def _generate_fatal_typedef_impl(self, sg, program, aliases, order, result):
        name = self._program.name
        safe_ns = self._get_namespace().replace('.', '_')
        annclsprefix = '{0}_{1}__struct_unique_annotations'.format(
            safe_ns, name)
        mnfclsprefix = '{0}_{1}__struct_unique_member_info_list'.format(
            safe_ns, name)
        with sg.namespace('apache.thrift.detail').scope as detail:
            for i, alias, ttype, ns_prefix in aliases:
                string_ref = self._set_fatal_string(i.symbolic)
                result[i.symbolic] = (i.symbolic, string_ref, string_ref)
                order.append(i.symbolic)
                detail('THRIFT_REGISTER_STRUCT_TRAITS(')
                # Struct
                detail('  {0}{1},'.format(ns_prefix, i.symbolic))
                # Name
                detail('  {0}{1},'.format(ns_prefix, self._get_fatal_string_id(
                    i.symbolic)))
                # MembersInfo
                detail('  {0}{1}::{2}_{3},'.format(
                    ns_prefix, self.fatal_detail_ns, i.symbolic, mnfclsprefix))
                # Info
                detail('  ::fatal::list<')
                annclsnm = '{0}{1}::{2}_{3}'.format(
                    ns_prefix, self.fatal_detail_ns, ttype.name, annclsprefix)
                for midx, m in enumerate(ttype.members):
                    detail('      {0}{1}::{2}_{3}::{4}{5}'.format(
                           ns_prefix, self.fatal_detail_ns,
                           i.symbolic, mnfclsprefix, m.name,
                           ',' if midx + 1 < len(ttype.members) else ''))
                detail('  >,')
                # MembersAnnotations
                detail('  {0}::members,'.format(annclsnm))
                # Metadata
                self._render_fatal_type_common_metadata(
                    annclsnm, ttype.type_id, detail, '  ', ns_prefix)
                detail(');')

        return (order, result)

    def _generate_fatal_constant(self, program):
        name = self._program.name
        ns = self._get_original_namespace()

        context = self._make_context(
            name + '_fatal_constant', tcc=False, impl=False,
            is_types_context=True)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_fatal_types.h')))
            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_constant_impl(sns, program)
            else:
                return self._generate_fatal_constant_impl(sg, program)

    def _generate_fatal_constant_impl(self, sns, program):
        result = []
        for i in program.consts:
            string_ref = self._set_fatal_string(i.name)
            result.append((i.name, string_ref, string_ref))
        return (result, result)

    def _generate_fatal_service(self, program):
        name = self._program.name
        ns = self._get_original_namespace()
        context = self._make_context(
            name + '_fatal_service', tcc=False, impl=False)
        with get_global_scope(CppPrimitiveFactory, context) as sg:
            sg('#include "{0}"'.format(self._with_include_prefix(
                self._program, name + '_types.h')))
            if self.flag_lean_mean_meta_machine:
                sg('#include "{0}"'.format(self._fatal_header()))

            sg()
            if len(ns) > 0:
                with sg.namespace(ns).scope as sns:
                    return self._generate_fatal_service_impl(sns, program)
            else:
                return self._generate_fatal_service_impl(sg, program)

    def _generate_fatal_service_impl(self, sns, program):
        with sns.namespace(self.fatal_detail_ns).scope:
            for s in program.services:
                for m in s.functions:
                    self._set_fatal_string(m.name)
                    for a in m.arglist.members:
                        self._set_fatal_string(a.name)
        result = []
        for i in program.services:
            string_ref = self._set_fatal_string(i.name)
            result.append((i.name, string_ref, string_ref))
        return (result, result)

    # ==========================================================================
    # FATAL REFLECTION CODE - end
    # ==========================================================================

    def _make_context(self, filename,
                      tcc=False,
                      separateclient=False,
                      custom_protocol=False,
                      impl=True,
                      is_types_context=False):
        'Convenience method to get the context and outputs for some file pair'
        # open files and instantiate outputs
        output_h = self._write_to(filename + '.h')
        output_impl = self._write_to(filename + '.cpp') if impl else None
        output_tcc = self._write_to(filename + '.tcc') if tcc else None
        additional_outputs = []
        custom_protocol_h = self._write_to(filename + "_custom_protocol.h") \
                if custom_protocol else None

        if separateclient:
            additional_outputs.append(self._write_to(filename + "_client.cpp"))
        header_path = self._with_include_prefix(self._program, filename)

        context = CppOutputContext(output_impl, output_h, output_tcc,
                                   header_path, additional_outputs,
                                   custom_protocol_h,
                                   is_types_context=is_types_context)

        print >>context.outputs, self._autogen_comment
        if context.custom_protocol_h is not None:
            print >>context.custom_protocol_h, self._autogen_comment
        return context

    @property
    def out_dir(self):
        return os.path.join(self._program.out_path, self._out_dir_base)

    def in_out_dir(self, filename):
        return os.path.join(self.out_dir, filename)

    # TODO add out_dir(self, program) as well?

    def _include_prefix(self, program, dir_base):
        #assert isinstance(program, frontend.t_program)
        ip = program.include_prefix
        if not self.flag_include_prefix or ip.startswith('/'):
            return ""
        if '/' in ip:
            ip = os.path.dirname(ip)
            return os.path.join(ip, dir_base)
        return ""

    def _out_include_prefix(self, program):
        return self._include_prefix(program, self._out_dir_base)

    def _with_include_prefix(self, program, *args):
        return os.path.join(self._out_include_prefix(program), *args)

    def _write_to(self, to):
        return IndentedOutput(open(self.in_out_dir(to), 'w'))

    def init_generator(self):
        name = self._program.name
        # Make output directory
        try:
            os.mkdir(self.out_dir)
        except OSError as exc:
            if exc.errno == errno.EEXIST and os.path.isdir(self.out_dir):
                pass
            else:
                raise

        self._const_scope = None

        # Only generate reflection files
        if self.flag_only_reflection:
            return;

        # open files and instantiate outputs for types
        context = self._make_context(name + '_types', tcc=True,
                                     custom_protocol=True,
                                     is_types_context=True)
        s = self._types_global = get_global_scope(CppPrimitiveFactory, context)

        self._types_out_impl = types_out_impl = context.impl
        self._out_tcc = types_out_tcc = context.tcc
        self._generated_types = []
        # Enter the scope (prints guard)
        s.acquire()

        # Include base types
        s('#include <thrift/lib/cpp2/Thrift.h>')
        s('#include <thrift/lib/cpp2/protocol/Protocol.h>')
        s('#include <thrift/lib/cpp/TApplicationException.h>')

        if self.flag_optionals:
            s('#include <folly/Optional.h>')
        s('#include <folly/io/IOBuf.h>')
        s('#include <folly/io/Cursor.h>')
        s()
        # Include other Thrift includes
        for inc in self._program.includes:
            s('#include "{0}_types.h"' \
              .format(self._with_include_prefix(inc, inc.name)))
            print >>context.custom_protocol_h, \
                    '#include "{0}_types_custom_protocol.h"'.format(
                            self._with_include_prefix(inc, inc.name))

        for _, b, _ in self.protocols:
            print >>types_out_tcc, \
                '#include <thrift/lib/cpp2/protocol/{0}.h>'. format(b)

        s("#include <thrift/lib/cpp2/GeneratedHeaderHelper.h>")
        s()

        # Include custom headers
        for inc in self._program.cpp_includes:
            if inc.startswith('<'):
                s('#include {0}'.format(inc))
            else:
                s('#include "{0}"'.format(inc))
        s()
        # The swap() code needs <algorithm> for std::swap()
        print >>types_out_impl, '#include <algorithm>\n'

        print >>types_out_impl, '#include <folly/Indestructible.h>\n'

        print >>types_out_impl, '#include "{}_data.h"\n'.format(
            self._with_include_prefix(self._program, name))
        print >>types_out_impl, '\n'

        fatal_header = '#include "{0}"'.format(self._fatal_header())
        if self.flag_lean_mean_meta_machine:
            print >>types_out_impl, fatal_header
            print >>context.custom_protocol_h, fatal_header
            print >>types_out_tcc, fatal_header
            print >>types_out_tcc, '#include <thrift/lib/cpp2/fatal/serializer.h>'

        # using directives
        s()

        # Open namespace
        s = self._types_scope = \
                s.namespace(self._get_namespace()).scope
        s.acquire()
        self._generate_indirection(self._program)

    def close_generator(self):
        # make sure that the main types namespace is closed
        self._types_scope.release()

        if self.flag_implicit_templates:
            # Include the types.tcc file from the types header file
            s = self._types_global
            s()
            s('#include "{0}_types.tcc"'.format(
                self._with_include_prefix(self._program, self._program.name)))

        self._types_global.release()


    def _generate_comment(self, text, style='auto'):
        'Style = block, line or auto'
        lines = text.split('\n')
        if style == 'auto':
            style = len(lines) > 1 and 'block' or 'line'
        if style == 'block':
            return '\n'.join(chain(('/**',), \
                    (' * {0}'.format(line) for line in lines),
                    (' */',)))
        elif style == 'line':
            return '\n'.join('// {0}'.format(line) for line in lines)
        else:
            raise NotImplementedError

    def _is_processed_in_eb(self, function):
        if function.annotations is not None and \
          'thread' in function.annotations.annotations:
            return function.annotations.annotations['thread'] == 'eb'
        return self.flag_process_in_event_base

# register the generator factory
t_generator.GeneratorFactory(CppGenerator)
